Skip to content

Commit 23923df

Browse files
committed
clang/csa: address review and try to use symbolic values to split state
1 parent 7b4f999 commit 23923df

File tree

3 files changed

+95
-43
lines changed

3 files changed

+95
-43
lines changed

clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ using namespace ento;
3030

3131
namespace {
3232

33+
QualType getSufficientTypeForOverflowOp(CheckerContext &C, const QualType &T) {
34+
assert(T->isIntegerType());
35+
ASTContext &ACtx = C.getASTContext();
36+
37+
unsigned BitWidth = ACtx.getIntWidth(T);
38+
return ACtx.getIntTypeForBitwidth(BitWidth * 2, T->isSignedIntegerType());
39+
}
40+
3341
QualType getOverflowBuiltinResultType(const CallEvent &Call) {
3442
assert(Call.getNumArgs() == 3);
3543

@@ -73,15 +81,18 @@ QualType getOverflowBuiltinResultType(const CallEvent &Call, CheckerContext &C,
7381
return getOverflowBuiltinResultType(Call);
7482
default:
7583
assert(false && "Unknown overflow builtin");
84+
return Ast.IntTy;
7685
}
7786
}
7887

7988
class BuiltinFunctionChecker : public Checker<eval::Call> {
8089
public:
8190
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
82-
void HandleOverflowBuiltin(const CallEvent &Call, CheckerContext &C,
91+
void handleOverflowBuiltin(const CallEvent &Call, CheckerContext &C,
8392
BinaryOperator::Opcode Op,
8493
QualType ResultType) const;
94+
std::pair<bool, bool> checkOverflow(CheckerContext &C, SVal RetVal,
95+
QualType Res) const;
8596

8697
private:
8798
// From: clang/include/clang/Basic/Builtins.def
@@ -101,7 +112,36 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
101112

102113
} // namespace
103114

104-
void BuiltinFunctionChecker::HandleOverflowBuiltin(const CallEvent &Call,
115+
std::pair<bool, bool>
116+
BuiltinFunctionChecker::checkOverflow(CheckerContext &C, SVal RetVal,
117+
QualType Res) const {
118+
ProgramStateRef State = C.getState();
119+
SValBuilder &SVB = C.getSValBuilder();
120+
auto SvalToBool = [&](SVal val) {
121+
return State->isNonNull(val).isConstrainedTrue();
122+
};
123+
ASTContext &ACtx = C.getASTContext();
124+
125+
assert(Res->isIntegerType());
126+
127+
unsigned BitWidth = ACtx.getIntWidth(Res);
128+
SVal MinType = nonloc::ConcreteInt(
129+
llvm::APSInt::getMinValue(BitWidth, Res->isUnsignedIntegerType()));
130+
SVal MaxType = nonloc::ConcreteInt(
131+
llvm::APSInt::getMaxValue(BitWidth, Res->isUnsignedIntegerType()));
132+
133+
bool IsGreaterMax =
134+
SvalToBool(SVB.evalBinOp(State, BO_GT, RetVal, MaxType, Res));
135+
bool IsLessMin =
136+
SvalToBool(SVB.evalBinOp(State, BO_LT, RetVal, MinType, Res));
137+
138+
bool IsLeMax = SvalToBool(SVB.evalBinOp(State, BO_LE, RetVal, MaxType, Res));
139+
bool IsGeMin = SvalToBool(SVB.evalBinOp(State, BO_GE, RetVal, MinType, Res));
140+
141+
return {IsGreaterMax || IsLessMin, IsLeMax && IsGeMin};
142+
}
143+
144+
void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call,
105145
CheckerContext &C,
106146
BinaryOperator::Opcode Op,
107147
QualType ResultType) const {
@@ -115,14 +155,12 @@ void BuiltinFunctionChecker::HandleOverflowBuiltin(const CallEvent &Call,
115155
SVal Arg1 = Call.getArgSVal(0);
116156
SVal Arg2 = Call.getArgSVal(1);
117157

158+
SVal RetValMax = SVB.evalBinOp(State, Op, Arg1, Arg2,
159+
getSufficientTypeForOverflowOp(C, ResultType));
118160
SVal RetVal = SVB.evalBinOp(State, Op, Arg1, Arg2, ResultType);
119161

120-
// TODO: Handle overflows with values that known to overflow. Like INT_MAX + 1
121-
// should not produce state for non-overflow case and threat it as
122-
// unreachable.
123-
124-
// Handle non-overflow case.
125-
{
162+
auto [Overflow, NotOverflow] = checkOverflow(C, RetValMax, ResultType);
163+
if (NotOverflow) {
126164
ProgramStateRef StateSuccess =
127165
State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(false));
128166

@@ -132,11 +170,9 @@ void BuiltinFunctionChecker::HandleOverflowBuiltin(const CallEvent &Call,
132170
C.addTransition(StateSuccess);
133171
}
134172

135-
// Handle overflow case.
136-
{
173+
if (Overflow)
137174
C.addTransition(
138175
State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(true)));
139-
}
140176
}
141177

142178
bool BuiltinFunctionChecker::isBuiltinLikeFunction(
@@ -183,7 +219,7 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
183219
case Builtin::BI__builtin_umul_overflow:
184220
case Builtin::BI__builtin_umull_overflow:
185221
case Builtin::BI__builtin_umulll_overflow:
186-
HandleOverflowBuiltin(Call, C, BO_Mul,
222+
handleOverflowBuiltin(Call, C, BO_Mul,
187223
getOverflowBuiltinResultType(Call, C, BI));
188224
return true;
189225
case Builtin::BI__builtin_sub_overflow:
@@ -193,7 +229,7 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
193229
case Builtin::BI__builtin_usub_overflow:
194230
case Builtin::BI__builtin_usubl_overflow:
195231
case Builtin::BI__builtin_usubll_overflow:
196-
HandleOverflowBuiltin(Call, C, BO_Sub,
232+
handleOverflowBuiltin(Call, C, BO_Sub,
197233
getOverflowBuiltinResultType(Call, C, BI));
198234
return true;
199235
case Builtin::BI__builtin_add_overflow:
@@ -203,7 +239,7 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
203239
case Builtin::BI__builtin_uadd_overflow:
204240
case Builtin::BI__builtin_uaddl_overflow:
205241
case Builtin::BI__builtin_uaddll_overflow:
206-
HandleOverflowBuiltin(Call, C, BO_Add,
242+
handleOverflowBuiltin(Call, C, BO_Add,
207243
getOverflowBuiltinResultType(Call, C, BI));
208244
return true;
209245
case Builtin::BI__builtin_assume:
Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,83 @@
11
// RUN: %clang_analyze_cc1 -triple x86_64-unknown-unknown -verify %s \
22
// RUN: -analyzer-checker=core,debug.ExprInspection
33

4-
#define NULL ((void *)0)
5-
#define INT_MAX __INT_MAX__
4+
#define __UINT_MAX__ (__INT_MAX__ * 2U + 1U)
65

76
void clang_analyzer_dump_int(int);
7+
void clang_analyzer_eval(int);
8+
void clang_analyzer_warnIfReached(void);
89

9-
void test1(void)
10+
void test_add_nooverflow(void)
1011
{
1112
int res;
1213

1314
if (__builtin_add_overflow(10, 20, &res)) {
14-
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
15+
clang_analyzer_warnIfReached();
1516
return;
1617
}
1718

18-
clang_analyzer_dump_int(res); //expected-warning{{30}}
19+
clang_analyzer_dump_int(res); //expected-warning{{30 S32b}}
1920
}
2021

21-
void test2(void)
22+
void test_add_overflow(void)
2223
{
2324
int res;
2425

25-
__builtin_add_overflow(10, 20, &res);
26-
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} expected-warning{{S32b}}
26+
if (__builtin_add_overflow(__INT_MAX__, 1, &res)) {
27+
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
28+
return;
29+
}
30+
31+
clang_analyzer_warnIfReached();
2732
}
2833

29-
void test3(void)
34+
void test_add_overflow_contraints(int a, int b)
3035
{
3136
int res;
3237

33-
if (__builtin_sub_overflow(10, 20, &res)) {
34-
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
38+
if (a != 10)
39+
return;
40+
if (b != 0)
41+
return;
42+
43+
if (__builtin_add_overflow(a, b, &res)) {
44+
clang_analyzer_warnIfReached();
3545
return;
3646
}
3747

38-
clang_analyzer_dump_int(res); //expected-warning{{-10}}
48+
clang_analyzer_dump_int(res); //expected-warning{{10 S32b}}
3949
}
4050

41-
void test4(void)
51+
void test_uaddll_overflow_contraints(unsigned long a, unsigned long b)
4252
{
43-
int res;
53+
unsigned long long res;
4454

45-
if (__builtin_sub_overflow(10, 20, &res)) {
46-
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
55+
if (a != 10)
56+
return;
57+
if (b != 10)
4758
return;
48-
}
4959

50-
if (res != -10) {
51-
*(volatile char *)NULL; //no warning
60+
if (__builtin_uaddll_overflow(a, b, &res)) {
61+
clang_analyzer_warnIfReached();
62+
return;
5263
}
5364
}
5465

55-
void test5(void)
66+
void test_uadd_overflow_contraints(unsigned a, unsigned b)
5667
{
57-
int res;
68+
unsigned res;
5869

59-
if (__builtin_mul_overflow(10, 20, &res)) {
60-
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
70+
if (a > 10)
6171
return;
62-
}
72+
if (b > 10)
73+
return;
74+
75+
// clang_analyzer_eval(a + b < 30); <--- Prints 1 and 0, but why ???
6376

64-
clang_analyzer_dump_int(res); //expected-warning{{200}}
77+
if (__builtin_uadd_overflow(a, b, &res)) {
78+
/* clang_analyzer_warnIfReached(); */
79+
return;
80+
}
6581
}
82+
83+
// TODO: more tests after figuring out what's going on.

clang/test/Analysis/out-of-bounds-diagnostics.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,14 +280,12 @@ int *mallocRegion(void) {
280280

281281
int *custom_calloc(size_t a, size_t b) {
282282
size_t res;
283-
if (__builtin_mul_overflow(a, b, &res))
284-
return 0;
285283

286-
return malloc(res);
284+
return __builtin_mul_overflow(a, b, &res) ? 0 : malloc(res);
287285
}
288286

289287
int *mallocRegionOverflow(void) {
290-
int *mem = (int*)custom_calloc(4, 10);
288+
int *mem = (int*)custom_calloc(10, sizeof(int));
291289

292290
mem[20] = 10;
293291
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}

0 commit comments

Comments
 (0)