Skip to content

Commit 7b4f999

Browse files
committed
clang/csa: add initial support for builtin overflow
1 parent f0df4fb commit 7b4f999

File tree

3 files changed

+204
-2
lines changed

3 files changed

+204
-2
lines changed

clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,67 @@
2121
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
2222
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2323
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24+
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
2425
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
26+
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
2527

2628
using namespace clang;
2729
using namespace ento;
2830

2931
namespace {
3032

33+
QualType getOverflowBuiltinResultType(const CallEvent &Call) {
34+
assert(Call.getNumArgs() == 3);
35+
36+
return Call.getArgExpr(2)->getType()->getPointeeType();
37+
}
38+
39+
QualType getOverflowBuiltinResultType(const CallEvent &Call, CheckerContext &C,
40+
unsigned BI) {
41+
assert(Call.getNumArgs() == 3);
42+
43+
ASTContext &Ast = C.getASTContext();
44+
45+
switch (BI) {
46+
case Builtin::BI__builtin_smul_overflow:
47+
case Builtin::BI__builtin_ssub_overflow:
48+
case Builtin::BI__builtin_sadd_overflow:
49+
return Ast.IntTy;
50+
case Builtin::BI__builtin_smull_overflow:
51+
case Builtin::BI__builtin_ssubl_overflow:
52+
case Builtin::BI__builtin_saddl_overflow:
53+
return Ast.LongTy;
54+
case Builtin::BI__builtin_smulll_overflow:
55+
case Builtin::BI__builtin_ssubll_overflow:
56+
case Builtin::BI__builtin_saddll_overflow:
57+
return Ast.LongLongTy;
58+
case Builtin::BI__builtin_umul_overflow:
59+
case Builtin::BI__builtin_usub_overflow:
60+
case Builtin::BI__builtin_uadd_overflow:
61+
return Ast.UnsignedIntTy;
62+
case Builtin::BI__builtin_umull_overflow:
63+
case Builtin::BI__builtin_usubl_overflow:
64+
case Builtin::BI__builtin_uaddl_overflow:
65+
return Ast.UnsignedLongTy;
66+
case Builtin::BI__builtin_umulll_overflow:
67+
case Builtin::BI__builtin_usubll_overflow:
68+
case Builtin::BI__builtin_uaddll_overflow:
69+
return Ast.UnsignedLongLongTy;
70+
case Builtin::BI__builtin_mul_overflow:
71+
case Builtin::BI__builtin_sub_overflow:
72+
case Builtin::BI__builtin_add_overflow:
73+
return getOverflowBuiltinResultType(Call);
74+
default:
75+
assert(false && "Unknown overflow builtin");
76+
}
77+
}
78+
3179
class BuiltinFunctionChecker : public Checker<eval::Call> {
3280
public:
3381
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
82+
void HandleOverflowBuiltin(const CallEvent &Call, CheckerContext &C,
83+
BinaryOperator::Opcode Op,
84+
QualType ResultType) const;
3485

3586
private:
3687
// From: clang/include/clang/Basic/Builtins.def
@@ -50,6 +101,44 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
50101

51102
} // namespace
52103

104+
void BuiltinFunctionChecker::HandleOverflowBuiltin(const CallEvent &Call,
105+
CheckerContext &C,
106+
BinaryOperator::Opcode Op,
107+
QualType ResultType) const {
108+
// All __builtin_*_overflow functions take 3 argumets.
109+
assert(Call.getNumArgs() == 3);
110+
111+
ProgramStateRef State = C.getState();
112+
SValBuilder &SVB = C.getSValBuilder();
113+
const Expr *CE = Call.getOriginExpr();
114+
115+
SVal Arg1 = Call.getArgSVal(0);
116+
SVal Arg2 = Call.getArgSVal(1);
117+
118+
SVal RetVal = SVB.evalBinOp(State, Op, Arg1, Arg2, ResultType);
119+
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+
{
126+
ProgramStateRef StateSuccess =
127+
State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(false));
128+
129+
if (auto L = Call.getArgSVal(2).getAs<Loc>())
130+
StateSuccess = StateSuccess->bindLoc(*L, RetVal, C.getLocationContext());
131+
132+
C.addTransition(StateSuccess);
133+
}
134+
135+
// Handle overflow case.
136+
{
137+
C.addTransition(
138+
State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(true)));
139+
}
140+
}
141+
53142
bool BuiltinFunctionChecker::isBuiltinLikeFunction(
54143
const CallEvent &Call) const {
55144
const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Call.getDecl());
@@ -82,10 +171,41 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
82171
return true;
83172
}
84173

85-
switch (FD->getBuiltinID()) {
174+
unsigned BI = FD->getBuiltinID();
175+
176+
switch (BI) {
86177
default:
87178
return false;
88-
179+
case Builtin::BI__builtin_mul_overflow:
180+
case Builtin::BI__builtin_smul_overflow:
181+
case Builtin::BI__builtin_smull_overflow:
182+
case Builtin::BI__builtin_smulll_overflow:
183+
case Builtin::BI__builtin_umul_overflow:
184+
case Builtin::BI__builtin_umull_overflow:
185+
case Builtin::BI__builtin_umulll_overflow:
186+
HandleOverflowBuiltin(Call, C, BO_Mul,
187+
getOverflowBuiltinResultType(Call, C, BI));
188+
return true;
189+
case Builtin::BI__builtin_sub_overflow:
190+
case Builtin::BI__builtin_ssub_overflow:
191+
case Builtin::BI__builtin_ssubl_overflow:
192+
case Builtin::BI__builtin_ssubll_overflow:
193+
case Builtin::BI__builtin_usub_overflow:
194+
case Builtin::BI__builtin_usubl_overflow:
195+
case Builtin::BI__builtin_usubll_overflow:
196+
HandleOverflowBuiltin(Call, C, BO_Sub,
197+
getOverflowBuiltinResultType(Call, C, BI));
198+
return true;
199+
case Builtin::BI__builtin_add_overflow:
200+
case Builtin::BI__builtin_sadd_overflow:
201+
case Builtin::BI__builtin_saddl_overflow:
202+
case Builtin::BI__builtin_saddll_overflow:
203+
case Builtin::BI__builtin_uadd_overflow:
204+
case Builtin::BI__builtin_uaddl_overflow:
205+
case Builtin::BI__builtin_uaddll_overflow:
206+
HandleOverflowBuiltin(Call, C, BO_Add,
207+
getOverflowBuiltinResultType(Call, C, BI));
208+
return true;
89209
case Builtin::BI__builtin_assume:
90210
case Builtin::BI__assume: {
91211
assert (Call.getNumArgs() > 0);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %clang_analyze_cc1 -triple x86_64-unknown-unknown -verify %s \
2+
// RUN: -analyzer-checker=core,debug.ExprInspection
3+
4+
#define NULL ((void *)0)
5+
#define INT_MAX __INT_MAX__
6+
7+
void clang_analyzer_dump_int(int);
8+
9+
void test1(void)
10+
{
11+
int res;
12+
13+
if (__builtin_add_overflow(10, 20, &res)) {
14+
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
15+
return;
16+
}
17+
18+
clang_analyzer_dump_int(res); //expected-warning{{30}}
19+
}
20+
21+
void test2(void)
22+
{
23+
int res;
24+
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}}
27+
}
28+
29+
void test3(void)
30+
{
31+
int res;
32+
33+
if (__builtin_sub_overflow(10, 20, &res)) {
34+
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
35+
return;
36+
}
37+
38+
clang_analyzer_dump_int(res); //expected-warning{{-10}}
39+
}
40+
41+
void test4(void)
42+
{
43+
int res;
44+
45+
if (__builtin_sub_overflow(10, 20, &res)) {
46+
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
47+
return;
48+
}
49+
50+
if (res != -10) {
51+
*(volatile char *)NULL; //no warning
52+
}
53+
}
54+
55+
void test5(void)
56+
{
57+
int res;
58+
59+
if (__builtin_mul_overflow(10, 20, &res)) {
60+
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
61+
return;
62+
}
63+
64+
clang_analyzer_dump_int(res); //expected-warning{{200}}
65+
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,23 @@ int *mallocRegion(void) {
278278
return mem;
279279
}
280280

281+
int *custom_calloc(size_t a, size_t b) {
282+
size_t res;
283+
if (__builtin_mul_overflow(a, b, &res))
284+
return 0;
285+
286+
return malloc(res);
287+
}
288+
289+
int *mallocRegionOverflow(void) {
290+
int *mem = (int*)custom_calloc(4, 10);
291+
292+
mem[20] = 10;
293+
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
294+
// expected-note@-2 {{Access of the heap area at index 20, while it holds only 10 'int' elements}}
295+
return mem;
296+
}
297+
281298
int *mallocRegionDeref(void) {
282299
int *mem = (int*)malloc(2*sizeof(int));
283300

0 commit comments

Comments
 (0)