Skip to content

Commit 792be5d

Browse files
author
Gabor Marton
committed
[analyzer][solver] Fix CmpOpTable handling bug
There is an error in the implementation of the logic of reaching the `Unknonw` tristate in CmpOpTable. ``` void cmp_op_table_unknownX2(int x, int y, int z) { if (x >= y) { // x >= y [1, 1] if (x + z < y) return; // x + z < y [0, 0] if (z != 0) return; // x < y [0, 0] clang_analyzer_eval(x > y); // expected-warning{{TRUE}} expected-warning{{FALSE}} } } ``` We miss the `FALSE` warning because the false branch is infeasible. We have to exploit simplification to discover the bug. If we had `x < y` as the second condition then the analyzer would return the parent state on the false path and the new constraint would not be part of the State. But adding `z` to the condition makes both paths feasible. The root cause of the bug is that we reach the `Unknown` tristate twice, but in both occasions we reach the same `Op` that is `>=` in the test case. So, we reached `>=` twice, but we never reached `!=`, thus querying the `Unknonw2x` column with `getCmpOpStateForUnknownX2` is wrong. The solution is to ensure that we reached both **different** `Op`s once. Differential Revision: https://reviews.llvm.org/D110910
1 parent b2c906d commit 792be5d

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ class SymbolicRangeInferrer
11121112
if (!SSE)
11131113
return llvm::None;
11141114

1115-
BinaryOperatorKind CurrentOP = SSE->getOpcode();
1115+
const BinaryOperatorKind CurrentOP = SSE->getOpcode();
11161116

11171117
// We currently do not support <=> (C++20).
11181118
if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
@@ -1126,7 +1126,12 @@ class SymbolicRangeInferrer
11261126

11271127
SymbolManager &SymMgr = State->getSymbolManager();
11281128

1129-
int UnknownStates = 0;
1129+
// We use this variable to store the last queried operator (`QueriedOP`)
1130+
// for which the `getCmpOpState` returned with `Unknown`. If there are two
1131+
// different OPs that returned `Unknown` then we have to query the special
1132+
// `UnknownX2` column. We assume that `getCmpOpState(CurrentOP, CurrentOP)`
1133+
// never returns `Unknown`, so `CurrentOP` is a good initial value.
1134+
BinaryOperatorKind LastQueriedOpToUnknown = CurrentOP;
11301135

11311136
// Loop goes through all of the columns exept the last one ('UnknownX2').
11321137
// We treat `UnknownX2` column separately at the end of the loop body.
@@ -1163,15 +1168,18 @@ class SymbolicRangeInferrer
11631168
CmpOpTable.getCmpOpState(CurrentOP, QueriedOP);
11641169

11651170
if (BranchState == OperatorRelationsTable::Unknown) {
1166-
if (++UnknownStates == 2)
1167-
// If we met both Unknown states.
1171+
if (LastQueriedOpToUnknown != CurrentOP &&
1172+
LastQueriedOpToUnknown != QueriedOP) {
1173+
// If we got the Unknown state for both different operators.
11681174
// if (x <= y) // assume true
11691175
// if (x != y) // assume true
11701176
// if (x < y) // would be also true
11711177
// Get a state from `UnknownX2` column.
11721178
BranchState = CmpOpTable.getCmpOpStateForUnknownX2(CurrentOP);
1173-
else
1179+
} else {
1180+
LastQueriedOpToUnknown = QueriedOP;
11741181
continue;
1182+
}
11751183
}
11761184

11771185
return (BranchState == OperatorRelationsTable::True) ? getTrueRange(T)

clang/test/Analysis/constraint_manager_conditions.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,17 @@ void comparison_le_ge(int x, int y) {
211211
clang_analyzer_eval(y != x); // expected-warning{{FALSE}}
212212
}
213213
}
214+
215+
// Test the logic of reaching the `Unknonw` tristate in CmpOpTable.
216+
void cmp_op_table_unknownX2(int x, int y, int z) {
217+
if (x >= y) {
218+
// x >= y [1, 1]
219+
if (x + z < y)
220+
return;
221+
// x + z < y [0, 0]
222+
if (z != 0)
223+
return;
224+
// x < y [0, 0]
225+
clang_analyzer_eval(x > y); // expected-warning{{TRUE}} expected-warning{{FALSE}}
226+
}
227+
}

0 commit comments

Comments
 (0)