Skip to content

Commit 23b88e8

Browse files
authored
[analyzer] Remove inaccurate legacy handling of bad bitwise shifts (llvm#66647)
Previously, bitwise shifts with constant operands were validated by the checker `core.UndefinedBinaryOperatorResult`. However, this logic was unreliable, and commit 25b9696 added the dedicated checker `core.BitwiseShift` which validated the preconditions of all bitwise shifts with a more accurate logic (that uses the real types from the AST instead of the unreliable type information encoded in `APSInt` objects). This commit disables the inaccurate logic that could mark bitwise shifts as 'undefined' and removes the redundant shift-related warning messages from core.UndefinedBinaryOperatorResult. The tests that were validating this logic are also deleted by this commit; but I verified that those testcases trigger the expected bug reports from `core.BitwiseShift`. (I didn't convert them to tests of `core.BitwiseShift`, because that checker already has its own extensive test suite with many analogous testcases.) I hope that there will be a time when the constant folding will be reliable, but until then we need hacky solutions like this improve the quality of results.
1 parent 22ebf1e commit 23b88e8

File tree

8 files changed

+39
-176
lines changed

8 files changed

+39
-176
lines changed

clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp

Lines changed: 3 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -115,59 +115,9 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
115115
OS << " due to array index out of bounds";
116116
} else {
117117
// Neither operand was undefined, but the result is undefined.
118-
if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
119-
B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
120-
C.isNegative(B->getRHS())) {
121-
OS << "The result of the "
122-
<< ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
123-
: "right")
124-
<< " shift is undefined because the right operand is negative";
125-
Ex = B->getRHS();
126-
} else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
127-
B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
128-
isShiftOverflow(B, C)) {
129-
130-
OS << "The result of the "
131-
<< ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
132-
: "right")
133-
<< " shift is undefined due to shifting by ";
134-
Ex = B->getRHS();
135-
136-
SValBuilder &SB = C.getSValBuilder();
137-
const llvm::APSInt *I =
138-
SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
139-
if (!I)
140-
OS << "a value that is";
141-
else if (I->isUnsigned())
142-
OS << '\'' << I->getZExtValue() << "\', which is";
143-
else
144-
OS << '\'' << I->getSExtValue() << "\', which is";
145-
146-
OS << " greater or equal to the width of type '"
147-
<< B->getLHS()->getType() << "'.";
148-
} else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
149-
C.isNegative(B->getLHS())) {
150-
OS << "The result of the left shift is undefined because the left "
151-
"operand is negative";
152-
Ex = B->getLHS();
153-
} else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
154-
isLeftShiftResultUnrepresentable(B, C)) {
155-
ProgramStateRef State = C.getState();
156-
SValBuilder &SB = C.getSValBuilder();
157-
const llvm::APSInt *LHS =
158-
SB.getKnownValue(State, C.getSVal(B->getLHS()));
159-
const llvm::APSInt *RHS =
160-
SB.getKnownValue(State, C.getSVal(B->getRHS()));
161-
OS << "The result of the left shift is undefined due to shifting \'"
162-
<< LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
163-
<< "\', which is unrepresentable in the unsigned version of "
164-
<< "the return type \'" << B->getLHS()->getType() << "\'";
165-
Ex = B->getLHS();
166-
} else {
167-
OS << "The result of the '"
168-
<< BinaryOperator::getOpcodeStr(B->getOpcode())
169-
<< "' expression is undefined";
170-
}
118+
OS << "The result of the '"
119+
<< BinaryOperator::getOpcodeStr(B->getOpcode())
120+
<< "' expression is undefined";
171121
}
172122
auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
173123
if (Ex) {

clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -280,14 +280,6 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
280280
if (Amt >= V1.getBitWidth())
281281
return nullptr;
282282

283-
if (!Ctx.getLangOpts().CPlusPlus20) {
284-
if (V1.isSigned() && V1.isNegative())
285-
return nullptr;
286-
287-
if (V1.isSigned() && Amt > V1.countl_zero())
288-
return nullptr;
289-
}
290-
291283
return &getValue( V1.operator<<( (unsigned) Amt ));
292284
}
293285

clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,21 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
529529

530530
const llvm::APSInt *Result =
531531
BasicVals.evalAPSInt(op, LHSValue, RHSValue);
532-
if (!Result)
532+
if (!Result) {
533+
if (op == BO_Shl || op == BO_Shr) {
534+
// FIXME: At this point the constant folding claims that the result
535+
// of a bitwise shift is undefined. However, constant folding
536+
// relies on the inaccurate type information that is stored in the
537+
// bit size of APSInt objects, and if we reached this point, then
538+
// the checker core.BitwiseShift already determined that the shift
539+
// is valid (in a PreStmt callback, by querying the real type from
540+
// the AST node).
541+
// To avoid embarrassing false positives, let's just say that we
542+
// don't know anything about the result of the shift.
543+
return UnknownVal();
544+
}
533545
return UndefinedVal();
546+
}
534547

535548
return nonloc::ConcreteInt(*Result);
536549
}

clang/test/Analysis/bitwise-ops-nocrash.c

Lines changed: 0 additions & 28 deletions
This file was deleted.

clang/test/Analysis/bitwise-ops.c

Lines changed: 0 additions & 60 deletions
This file was deleted.

clang/test/Analysis/bitwise-shift-sanity-checks.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core \
22
// RUN: -analyzer-config core.BitwiseShift:Pedantic=true \
33
// RUN: -verify=expected,pedantic \
44
// RUN: -triple x86_64-pc-linux-gnu -x c %s \
55
// RUN: -Wno-shift-count-negative -Wno-shift-negative-value \
66
// RUN: -Wno-shift-count-overflow -Wno-shift-overflow \
77
// RUN: -Wno-shift-sign-overflow
88
//
9-
// RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \
9+
// RUN: %clang_analyze_cc1 -analyzer-checker=core \
1010
// RUN: -analyzer-config core.BitwiseShift:Pedantic=true \
1111
// RUN: -verify=expected,pedantic \
1212
// RUN: -triple x86_64-pc-linux-gnu -x c++ -std=c++14 %s \
1313
// RUN: -Wno-shift-count-negative -Wno-shift-negative-value \
1414
// RUN: -Wno-shift-count-overflow -Wno-shift-overflow \
1515
// RUN: -Wno-shift-sign-overflow
1616
//
17-
// RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \
17+
// RUN: %clang_analyze_cc1 -analyzer-checker=core \
1818
// RUN: -verify=expected \
1919
// RUN: -triple x86_64-pc-linux-gnu -x c++ -std=c++20 %s \
2020
// RUN: -Wno-shift-count-negative -Wno-shift-negative-value \
@@ -23,6 +23,8 @@
2323

2424
// This test file verifies that the BitwiseShift checker does not crash or
2525
// report false positives (at least on the cases that are listed here...)
26+
// Other core checkers are also enabled to see interactions with e.g.
27+
// core.UndefinedBinaryOperatorResult.
2628
// For the sake of brevity, 'note' output is not checked in this file.
2729

2830
// TEST OBVIOUSLY CORRECT CODE
@@ -116,3 +118,17 @@ void doubles_cast_to_integer(int *c) {
116118
*c = ((int)1.5) << 1; // no-crash
117119
*c = ((int)1.5) << (int)1.5; // no-crash
118120
}
121+
122+
// TEST CODE THAT WAS TRIGGERING BUGS IN EARLIER REVISIONS
123+
//===----------------------------------------------------------------------===//
124+
125+
unsigned int strange_cast(unsigned short sh) {
126+
// This testcase triggers a bug in the constant folding (it "forgets" the
127+
// cast), which is silenced in SimpleSValBuilder::evalBinOpNN() with an ugly
128+
// workaround, because otherwise it would lead to a false positive from
129+
// core.UndefinedBinaryOperatorResult.
130+
unsigned int i;
131+
sh++;
132+
for (i=0; i<sh; i++) {}
133+
return (unsigned int) ( ((unsigned int) sh) << 16 ); // no-warning
134+
}

clang/test/Analysis/left-shift-cxx2a.cpp

Lines changed: 0 additions & 22 deletions
This file was deleted.

clang/test/Analysis/svalbuilder-simplify-no-crash.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
// Here, we test that svalbuilder simplification does not produce any
77
// assertion failure.
88

9+
// expected-no-diagnostics
10+
911
void crashing(long a, _Bool b) {
1012
(void)(a & 1 && 0);
1113
b = a & 1;
12-
(void)(b << 1); // expected-warning{{core.UndefinedBinaryOperatorResult}}
14+
(void)(b << 1);
1315
}

0 commit comments

Comments
 (0)