Skip to content

[analyzer] Fix assertion failure in CXXInstanceCall::getCXXThisVal #70837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 4, 2023
Merged

[analyzer] Fix assertion failure in CXXInstanceCall::getCXXThisVal #70837

merged 3 commits into from
Nov 4, 2023

Conversation

steakhal
Copy link
Contributor

@steakhal steakhal commented Oct 31, 2023

Workaround the case when the this pointer is actually a NonLoc, by returning Unknown instead.
The solution isn't ideal, as this should be really a Loc, but due to how casts work, I feel this is our easiest and best option.

As this patch presents, I'm evaluating a cast to transform the NonLoc.
However, given that evalCast() can't be cast from NonLoc to a pointer type thingy (Loc), we end up with Unknown.
It is because EvalCastVisitor::VisitNonLocSymbolVal() only evaluates casts that happen from NonLoc to NonLocs.

When I tried to actually implement that case, I figured:

  1. Create a SymbolicRegion from that nonloc::SymbolVal; but SymbolRegion ctor expects a pointer type for the symbol.
  2. Okay, just have a SymbolCast, getting us the pointer type; but SymbolRegion expects SymbolData symbols, not generic SymExprs, as stated:

// Because pointer arithmetic is represented by ElementRegion layers,
// the base symbol here should not contain any arithmetic.

  1. We can't use ElementRegions to perform this cast because to have an ElementRegion, you already have to have a SubRegion that you want to cast, but the point is that we don't have that.

At this point, I gave up, and just left a FIXME instead, while still returning Unknown on that path.
IMO this is still better than having a crash.

Fixes #69922

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html labels Oct 31, 2023
@llvmbot
Copy link
Member

llvmbot commented Oct 31, 2023

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Balazs Benics (steakhal)

Changes

Workaround the case when the this pointer is actually a NonLoc, by returning Unknown instead.
The solution isn't ideal, as this should be really a Loc, but due to how casts work, I feel this is our easiest and best option.

I've considered using SVB.evalCast(ThisVal, Base->getType(), QualType()), but it doesn't work as EvalCastVisitor::VisitNonLocSymbolVal() only evaluates casts that happen from NonLoc to NonLocs.

When I tried to actually implement that case, I figured:

  1. Create a SymbolicRegion from that nonloc::SymbolVal; but SymbolRegion ctor expects a pointer type for the symbol.
  2. Okay, just have a SymbolCast, getting us the pointer type; but SymbolRegion expects SymbolData symbols, not generic SymExprs, as stated:

> // Because pointer arithmetic is represented by ElementRegion layers,
> // the base symbol here should not contain any arithmetic.

  1. We can't use ElementRegions to perform this cast because to have an ElementRegion, you already have to have a SubRegion that you want to cast, but the point is that we don't have that.

At this point, I gave up, and just returned Unknown xD IMO this is still better than having a crash.

Fixes #69922


Full diff: https://github.com/llvm/llvm-project/pull/70837.diff

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Core/CallEvent.cpp (+2-3)
  • (modified) clang/test/Analysis/builtin_bitcast.cpp (+21)
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index ad5bb66c4fff3c8..20bc64dc2631cec 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -715,10 +715,9 @@ void CXXInstanceCall::getExtraInvalidatedValues(
 SVal CXXInstanceCall::getCXXThisVal() const {
   const Expr *Base = getCXXThisExpr();
   // FIXME: This doesn't handle an overloaded ->* operator.
-  if (!Base)
+  SVal ThisVal = Base ? getSVal(Base) : UnknownVal();
+  if (isa<NonLoc>(ThisVal))
     return UnknownVal();
-
-  SVal ThisVal = getSVal(Base);
   assert(ThisVal.isUnknownOrUndef() || isa<Loc>(ThisVal));
   return ThisVal;
 }
diff --git a/clang/test/Analysis/builtin_bitcast.cpp b/clang/test/Analysis/builtin_bitcast.cpp
index 396e7caa45f6acd..13475694d287a22 100644
--- a/clang/test/Analysis/builtin_bitcast.cpp
+++ b/clang/test/Analysis/builtin_bitcast.cpp
@@ -30,3 +30,24 @@ void test(int i) {
   clang_analyzer_dump(g4);
   // expected-warning@-1 {{&i [as 64 bit integer]}}
 }
+
+struct A {
+  int n;
+  void set(int x) {
+    n = x;
+  }
+};
+using ptr_size = decltype(sizeof(void *));
+void gh_69922(ptr_size p) {
+  // expected-warning-re@+1 {{(reg_${{[0-9]+}}<ptr_size p>) & 1U}}
+  clang_analyzer_dump(__builtin_bit_cast(A*, p & 1));
+
+  __builtin_bit_cast(A*, p & 1)->set(2); // no-crash
+  // However, since the `this` pointer is expected to be a Loc, but we have
+  // NonLoc there, we simply give up and resolve it as `Unknown`.
+  // Then, inside the `set()` member function call we can't evaluate the
+  // store to the member variable `n`.
+
+  clang_analyzer_dump(__builtin_bit_cast(A*, p & 1)->n); // Ideally, this should print "2".
+  // expected-warning-re@-1 {{(reg_${{[0-9]+}}<ptr_size p>) & 1U}}
+}

@Xazax-hun
Copy link
Collaborator

Hmm, I wonder if we should leave a FIXME comment, but it looks good to me.

@steakhal
Copy link
Contributor Author

Hmm, I wonder if we should leave a FIXME comment, but it looks good to me.

I was thinking where to put the FIXME, and as I explored that should be within the CastVisitor. After that, I argued, that then I should still have the (ineffective) SVB.evalCast() to actually exercise that path.
It should be a fancy way of returning Unknown now, but arguably for the better reason xD

@Xazax-hun WDYT?

@steakhal steakhal requested a review from Xazax-hun October 31, 2023 18:25
@steakhal steakhal changed the title [analyzer] Fix assertion failure in CXXInstanceCall::getCXXThisVal [analyzer] Fix assertion failure in CXXInstanceCall::getCXXThisVal Oct 31, 2023
@Xazax-hun
Copy link
Collaborator

WDYT?

I like this! I hope we do not add too much redundant work, but at least we make it clear what is the plan to fix this in the future.

@steakhal
Copy link
Contributor Author

WDYT?

I like this! I hope we do not add too much redundant work, but at least we make it clear what is the plan to fix this in the future.

Please approve the PR again, so that I could merge this after I give some time for others to look at this.

Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically LGTM, thanks for fixing this crash.

I don't give a formal approval because I didn't independently verify the correctness of the details, but I like the direction of the change.

Workaround the case when the `this` pointer is actually a `NonLoc`, by
returning `Unknown` instead.
The solution isn't ideal, as `this` should be really a `Loc`, but due to
how casts work, I feel this is our easiest and best option.

I've considered using `SVB.evalCast(ThisVal, Base->getType(), QualType())`,
but it doesn't work as `EvalCastVisitor::VisitNonLocSymbolVal()` only
evaluates casts that happen from NonLoc to NonLocs.

When I tried to actually implement that case, I figured:
1) Create a SymbolicRegion from that nonloc::SymbolVal; but SymbolRegion
   ctor expects a pointer type for the symbol.
2) Okay, just have a SymbolCast, getting us the pointer type; but
   SymbolRegion expects SymbolData symbols, not generic SymExprs, as stated:

> // Because pointer arithmetic is represented by ElementRegion layers,
> // the base symbol here should not contain any arithmetic.

3) We can't use ElementRegions to perform this cast because to have an
   ElementRegion, you already have to have a SubRegion that you want to
   cast, but the point is that we don't have that.

At this point, I gave up, and just returned `Unknown` xD
IMO this is still better than having a crash.

Fixes #69922
@steakhal steakhal merged commit 51d15d1 into llvm:main Nov 4, 2023
@steakhal steakhal deleted the fix-gh-69922 branch November 4, 2023 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clang static analysis assert in CXXInstanceCall::getCXXThisVal
4 participants