Author: Balázs Benics
Date: 2026-08-27T08:32:17+01:00
New Revision: 959e8e7c74a341dadca6ec7106cc2eae11a5f692

URL: 
https://github.com/llvm/llvm-project/commit/959e8e7c74a341dadca6ec7106cc2eae11a5f692
DIFF: 
https://github.com/llvm/llvm-project/commit/959e8e7c74a341dadca6ec7106cc2eae11a5f692.diff

LOG: [analyzer] Fix assertion in BitwiseShiftChecker from getExtValue (#218940)

`APSInt::getExtValue()` asserts `isRepresentableByInt64()`, which for an
unsigned value means it must fit in 63 bits. `checkOvershift()` called
it unguarded on a solver-derived lower bound, so any bound at or above
2^63 crashed with:
`Assertion 'isRepresentableByInt64() && "Too many bits for int64_t"'
failed.`

```c++
unsigned huge_right_operand_symbolic(unsigned x, unsigned long long t) {
  if (t < 18000000000000000000ULL)
    return 0;
  return x >> t; // no-crash: gh #218867
}
```

Format the `APSInt` directly instead. Its stream operator honors the
value's own signedness, so bounds of any width print correctly, matching
how this file already renders `ConcreteInt` operands.

Note that no `_BitInt` is needed to reach this: a plain `unsigned long
long` suffices, as above. On no-assert builds the bug was silent
corruption rather than a crash, emitting a wrapped negative lower bound
for an unsigned value.

The assertion failure was surfaced by #209048 (in clang-23), which
taught the solver to propagate operand ranges through {+,-,*} and so
made these tight, large bounds reachable. But the defect is older:
#74141 introduced the bad call in clang-18.

The new test also pins a pre-existing, unrelated bug: the `Assuming`
path note routes the literal through `APInt`'s stream operator, which
always formats as signed, so `18000000000000000000` prints as a bogus
negative. That one is in `ConditionBRVisitor::patternMatch` and is
display-only -- the value never feeds the analysis. Printing it unsigned
regresses `Analysis/diagnostics/macros.cpp`, where an `int` compared
against `UINT32_MAX` should read `-1`, so the correct signedness depends
on the comparison's converted type rather than the literal's. Left as a
FIXME.

Fixes #218867

Assisted-By: claude

Added: 
    

Modified: 
    clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp
    clang/test/Analysis/bitwise-shift-common.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp
index fefd4dca1a08e..3e380fbaae528 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp
@@ -177,7 +177,7 @@ BugReportPtr BitwiseShiftValidator::checkOvershift() {
     SValBuilder &SVB = Ctx.getSValBuilder();
     if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right);
         MinRight && *MinRight >= LHSBitWidth) {
-      LowerBoundStr = formatv(" >= {0},", MinRight->getExtValue());
+      LowerBoundStr = formatv(" >= {0},", *MinRight);
     }
   }
 

diff  --git a/clang/test/Analysis/bitwise-shift-common.c 
b/clang/test/Analysis/bitwise-shift-common.c
index c5a9f4bcdde3c..de83f71ec068b 100644
--- a/clang/test/Analysis/bitwise-shift-common.c
+++ b/clang/test/Analysis/bitwise-shift-common.c
@@ -90,6 +90,19 @@ int too_large_right_operand_symbolic(int left, int right) {
   // expected-note@-2 {{The result of right shift is undefined because the 
right operand is >= 32, not smaller than 32, the capacity of 'int'}}
 }
 
+unsigned huge_right_operand_symbolic(unsigned x, unsigned long long t) {
+  // FIXME: the 'Assuming' note below prints the literal via APInt's stream
+  // operator, which always formats as signed, so 18000000000000000000 comes
+  // out as a bogus negative. That is a bug in ConditionBRVisitor.
+  // expected-note@+2 {{Assuming 't' is >= -446744073709551616}}
+  // expected-note@+1 {{Taking false branch}}
+  if (t < 18000000000000000000ULL)
+    return 0;
+  return x >> t; // no-crash: gh #218867
+  // expected-warning@-1 {{Right shift overflows the capacity of 'unsigned 
int'}}
+  // expected-note@-2 {{The result of right shift is undefined because the 
right operand is >= 18000000000000000000, not smaller than 32, the capacity of 
'unsigned int'}}
+}
+
 void clang_analyzer_value(int);
 int too_large_right_operand_compound(unsigned short arg) {
   // Note: this would be valid code with an 'unsigned int' because


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to