-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[analyzer] Fix crash on dereference invalid return value of getAdjustedParameterIndex() #83585
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
[analyzer] Fix crash on dereference invalid return value of getAdjustedParameterIndex() #83585
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-static-analyzer-1 Author: Exile (mzyKi) Changesfix #78810 Full diff: https://github.com/llvm/llvm-project/pull/83585.diff 2 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 504fd7f05e0f99..dc72945d68d56f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -354,8 +354,13 @@ SVal ExprEngine::computeObjectUnderConstruction(
// Operator arguments do not correspond to operator parameters
// because this-argument is implemented as a normal argument in
// operator call expressions but not in operator declarations.
- const TypedValueRegion *TVR = Caller->getParameterLocation(
- *Caller->getAdjustedParameterIndex(Idx), BldrCtx->blockCount());
+ std::optional<unsigned int> Index =
+ Caller->getAdjustedParameterIndex(Idx);
+ if (!Index) {
+ return std::nullopt;
+ }
+ const TypedValueRegion *TVR =
+ Caller->getParameterLocation(*Index, BldrCtx->blockCount());
if (!TVR)
return std::nullopt;
diff --git a/clang/test/Analysis/engine/expr-engine-cxx-crash.cpp b/clang/test/Analysis/engine/expr-engine-cxx-crash.cpp
new file mode 100644
index 00000000000000..fa0718668a75be
--- /dev/null
+++ b/clang/test/Analysis/engine/expr-engine-cxx-crash.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero -std=c++23 -verify %s
+// expected-no-diagnostics
+
+struct S
+{
+ constexpr auto operator==(this auto, S)
+ {
+ return true;
+ }
+};
+
+int main()
+{
+ return S {} == S {};
+}
+
+// test
\ No newline at end of file
|
f0291dd
to
6a9f7a9
Compare
Add suggested reviewers for this PR. I am unfamiliar with the engine code.
Please do not force push to your forked repo later after receiving suggestions from other reviewers. This will make previous suggestions hard to track. We have the squash and merge functionality to merge all the commits on your branch into one commit in the LLVM repo when everything is done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it's better than crashing, but I suspect it only resolves that, right?
Thank you for creating the ticket, and fixing the issue. I am a bit concerned that the fix is hiding the symptoms and not the actual bug. After a bit of checking, I think the issue is caused by the fact that for the explicit object functions, we are creating a if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
if (MD->isInstance()) // <-- The root of the issue
return create<CXXMemberOperatorCall>(OpCE, State, LCtx, ElemRef);
} I think it would be fixed by replacing That would make the behavior consistent with the behavior for other explicit object functions (deducing this). struct Foo {
void foo();
void bar(this Foo&);
};
void test(Foo f) {
f.foo(); // represented as `CXXMemberCallExpr`, and creates`CXXMemberCall` event
f.bar(); // represented as `CallExpr`, and creates `SimpleFunctionCall` event
} Live link here: https://godbolt.org/z/sjEbb6rMe. Would you be willing to adjust PR to implement the suggested change? |
Thanks for your help very much.I only fixed the symptoms of this bug without spending much time to find the root cause. I feel ashamed. But I am not sure whether this change has some side effects. |
There is nothing to be ashamed of. You have spent the time to debug the crash, and localize the use of non-engaged optional, this is already helpful for the project. And addressing the crash (for example by stopping analysis) makes sure, that analysis continues and we are able to report issues from other functions. This is an improvement over the status quo, and sometimes determining the root cause is very difficult.
There are 4 possible choices on how an operator can be overloaded in C++: When the Before C++23, the option didn't exist, and We may still not cover the case struct C {
static void operator()(int x) { return 10 / x; }
};
void test(C c) {
c(10);
} If you would be interested in looking at also fixing it after this PR, I would be happy to provide my thoughts, on how I would approach it. |
return value of getAdjustedParameterIndex()
a027604
to
5ac52a8
Compare
Thanks for your explanation and I'm willing to do the work later.I also hope that you will give me some suggestions about that. struct C {
static int operator()(int x) { return 10 / x; }
};
void test(C c) {
c(0);
} run command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The patch looks a lot better this way. Thanks!
I'd recommend adding this test to the existing clang/test/Analysis/cxx2b-deducing-this.cpp
test file to have all deducing this testcases at a common place.
While moving the test case to
This is separate from deducing this, so let's put in in a separate PR, and finish this first. |
Looking at the examples for the static operator at (live example): struct S {
int pad;
static void operator()(int x, int y) {
// clang_analyzer_dump(x);
// clang_analyzer_dump(y);
}
};
void calls() {
S s{10};
void (*fptr)(int, int) = &S::operator();
s(1, 2); // A
S::operator()(1, 2); // B
fptr(1, 2); // C
} You can experiment with the results of the dumps to check current behavior - you need to add The calls However, in the case of
So in this case we also need an adjustment when mapping from arguments to parameters. However, we cannot use
[ Note: We do not copy any function related to Then we need to add a new enum CallEventKind {
CE_Function,
CE_CXXStaticOperator,
CE_CXXMember,
CE_CXXMemberOperator,
CE_CXXDestructor,
CE_BEG_CXX_INSTANCE_CALLS = CE_CXXMember,
CE_END_CXX_INSTANCE_CALLS = CE_CXXDestructor,
....
}; Finally, we can change
Hope this helps. However, please open such changes in a separate PR. |
Backporting this in PR #84194 to clang-18. |
Made by following: #83585 (comment) Thanks for the details Tomek! CPP-5080
Made by following: llvm#83585 (comment) Thanks for the details Tomek! CPP-5080
…erator member function with explicit this (#132581) Fixes #116372 From this PR #83585, CSA starts to model overload operator member function with explicit this as `SimpleFunctionCall` rather than `CXXMemberOperatorCall` (derived from `CXXInstanceCall`), so `CXXInstanceCall` only represents a non-static C++ member function call `with implicit this`. For this checker, it models `operator=` for STL containers, which always uses implicit this, so the situation using explicit this can be skipped directly.
fix #78810
thanks for @Snape3058 's comment