Skip to content

Commit 060f955

Browse files
authored
[clang][analyzer] Fix error path of builtin overflow (llvm#136345)
According to https://clang.llvm.org/docs/LanguageExtensions.html#checked-arithmetic-builtins, result of builtin_*_overflow functions will be initialized even in case of overflow. Align analyzer logic to docs and always initialize 3rd argument of such builtins. Closes llvm#136292
1 parent fc71b2c commit 060f955

File tree

3 files changed

+58
-44
lines changed

3 files changed

+58
-44
lines changed

clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp

+48-38
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,14 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
9696
void handleOverflowBuiltin(const CallEvent &Call, CheckerContext &C,
9797
BinaryOperator::Opcode Op,
9898
QualType ResultType) const;
99-
const NoteTag *createBuiltinNoOverflowNoteTag(CheckerContext &C,
100-
bool BothFeasible, SVal Arg1,
101-
SVal Arg2, SVal Result) const;
102-
const NoteTag *createBuiltinOverflowNoteTag(CheckerContext &C) const;
99+
const NoteTag *createBuiltinOverflowNoteTag(CheckerContext &C,
100+
bool BothFeasible, SVal Arg1,
101+
SVal Arg2, SVal Result) const;
102+
ProgramStateRef initStateAftetBuiltinOverflow(CheckerContext &C,
103+
ProgramStateRef State,
104+
const CallEvent &Call,
105+
SVal RetCal,
106+
bool IsOverflow) const;
103107
std::pair<bool, bool> checkOverflow(CheckerContext &C, SVal RetVal,
104108
QualType Res) const;
105109

@@ -121,30 +125,24 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
121125

122126
} // namespace
123127

124-
const NoteTag *BuiltinFunctionChecker::createBuiltinNoOverflowNoteTag(
125-
CheckerContext &C, bool BothFeasible, SVal Arg1, SVal Arg2,
126-
SVal Result) const {
127-
return C.getNoteTag([Result, Arg1, Arg2, BothFeasible](
128-
PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
128+
const NoteTag *BuiltinFunctionChecker::createBuiltinOverflowNoteTag(
129+
CheckerContext &C, bool overflow, SVal Arg1, SVal Arg2, SVal Result) const {
130+
return C.getNoteTag([Result, Arg1, Arg2, overflow](PathSensitiveBugReport &BR,
131+
llvm::raw_ostream &OS) {
129132
if (!BR.isInteresting(Result))
130133
return;
131134

132-
// Propagate interestingness to input argumets if result is interesting.
135+
// Propagate interestingness to input arguments if result is interesting.
133136
BR.markInteresting(Arg1);
134137
BR.markInteresting(Arg2);
135138

136-
if (BothFeasible)
139+
if (overflow)
140+
OS << "Assuming overflow";
141+
else
137142
OS << "Assuming no overflow";
138143
});
139144
}
140145

141-
const NoteTag *
142-
BuiltinFunctionChecker::createBuiltinOverflowNoteTag(CheckerContext &C) const {
143-
return C.getNoteTag([](PathSensitiveBugReport &BR,
144-
llvm::raw_ostream &OS) { OS << "Assuming overflow"; },
145-
/*isPrunable=*/true);
146-
}
147-
148146
std::pair<bool, bool>
149147
BuiltinFunctionChecker::checkOverflow(CheckerContext &C, SVal RetVal,
150148
QualType Res) const {
@@ -174,6 +172,29 @@ BuiltinFunctionChecker::checkOverflow(CheckerContext &C, SVal RetVal,
174172
return {MayOverflow || MayUnderflow, MayNotOverflow && MayNotUnderflow};
175173
}
176174

175+
ProgramStateRef BuiltinFunctionChecker::initStateAftetBuiltinOverflow(
176+
CheckerContext &C, ProgramStateRef State, const CallEvent &Call,
177+
SVal RetVal, bool IsOverflow) const {
178+
SValBuilder &SVB = C.getSValBuilder();
179+
SVal Arg1 = Call.getArgSVal(0);
180+
SVal Arg2 = Call.getArgSVal(1);
181+
auto BoolTy = C.getASTContext().BoolTy;
182+
183+
ProgramStateRef NewState =
184+
State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
185+
SVB.makeTruthVal(IsOverflow, BoolTy));
186+
187+
if (auto L = Call.getArgSVal(2).getAs<Loc>()) {
188+
NewState = NewState->bindLoc(*L, RetVal, C.getLocationContext());
189+
190+
// Propagate taint if any of the arguments were tainted
191+
if (isTainted(State, Arg1) || isTainted(State, Arg2))
192+
NewState = addTaint(NewState, *L);
193+
}
194+
195+
return NewState;
196+
}
197+
177198
void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call,
178199
CheckerContext &C,
179200
BinaryOperator::Opcode Op,
@@ -183,8 +204,6 @@ void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call,
183204

184205
ProgramStateRef State = C.getState();
185206
SValBuilder &SVB = C.getSValBuilder();
186-
const Expr *CE = Call.getOriginExpr();
187-
auto BoolTy = C.getASTContext().BoolTy;
188207

189208
SVal Arg1 = Call.getArgSVal(0);
190209
SVal Arg2 = Call.getArgSVal(1);
@@ -194,29 +213,20 @@ void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call,
194213
SVal RetVal = SVB.evalBinOp(State, Op, Arg1, Arg2, ResultType);
195214

196215
auto [Overflow, NotOverflow] = checkOverflow(C, RetValMax, ResultType);
197-
if (NotOverflow) {
198-
ProgramStateRef StateNoOverflow = State->BindExpr(
199-
CE, C.getLocationContext(), SVB.makeTruthVal(false, BoolTy));
200-
201-
if (auto L = Call.getArgSVal(2).getAs<Loc>()) {
202-
StateNoOverflow =
203-
StateNoOverflow->bindLoc(*L, RetVal, C.getLocationContext());
204216

205-
// Propagate taint if any of the argumets were tainted
206-
if (isTainted(State, Arg1) || isTainted(State, Arg2))
207-
StateNoOverflow = addTaint(StateNoOverflow, *L);
208-
}
217+
if (NotOverflow) {
218+
auto NewState =
219+
initStateAftetBuiltinOverflow(C, State, Call, RetVal, false);
209220

210-
C.addTransition(
211-
StateNoOverflow,
212-
createBuiltinNoOverflowNoteTag(
213-
C, /*BothFeasible=*/NotOverflow && Overflow, Arg1, Arg2, RetVal));
221+
C.addTransition(NewState, createBuiltinOverflowNoteTag(
222+
C, /*overflow=*/false, Arg1, Arg2, RetVal));
214223
}
215224

216225
if (Overflow) {
217-
C.addTransition(State->BindExpr(CE, C.getLocationContext(),
218-
SVB.makeTruthVal(true, BoolTy)),
219-
createBuiltinOverflowNoteTag(C));
226+
auto NewState = initStateAftetBuiltinOverflow(C, State, Call, RetVal, true);
227+
228+
C.addTransition(NewState, createBuiltinOverflowNoteTag(C, /*overflow=*/true,
229+
Arg1, Arg2, RetVal));
220230
}
221231
}
222232

clang/test/Analysis/builtin_overflow.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void test_add_overflow(void)
2626
int res;
2727

2828
if (__builtin_add_overflow(__INT_MAX__, 1, &res)) {
29-
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
29+
clang_analyzer_dump_int(res); //expected-warning{{-2147483648 S32b}}
3030
return;
3131
}
3232

@@ -38,7 +38,7 @@ void test_add_underoverflow(void)
3838
int res;
3939

4040
if (__builtin_add_overflow(__INT_MIN__, -1, &res)) {
41-
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
41+
clang_analyzer_dump_int(res); //expected-warning{{2147483647 S32b}}
4242
return;
4343
}
4444

@@ -160,7 +160,7 @@ void test_bool_assign(void)
160160
{
161161
int res;
162162

163-
// Reproduce issue from GH#111147. __builtin_*_overflow funcions
163+
// Reproduce issue from GH#111147. __builtin_*_overflow functions
164164
// should return _Bool, but not int.
165165
_Bool ret = __builtin_mul_overflow(10, 20, &res); // no crash
166166
}

clang/test/Analysis/builtin_overflow_notes.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ void test_no_overflow_note(int a, int b)
1919

2020
void test_overflow_note(int a, int b)
2121
{
22-
int res; // expected-note{{'res' declared without an initial value}}
22+
int res;
2323

2424
if (__builtin_add_overflow(a, b, &res)) { // expected-note {{Assuming overflow}}
2525
// expected-note@-1 {{Taking true branch}}
26-
int var = res; // expected-warning{{Assigned value is uninitialized}}
27-
// expected-note@-1 {{Assigned value is uninitialized}}
26+
if (res) { // expected-note {{Assuming 'res' is not equal to 0}}
27+
// expected-note@-1 {{Taking true branch}}
28+
int *ptr = 0; // expected-note {{'ptr' initialized to a null pointer value}}
29+
int var = *(int *) ptr; //expected-warning {{Dereference of null pointer}}
30+
//expected-note@-1 {{Dereference of null pointer}}
31+
}
2832
return;
2933
}
3034
}

0 commit comments

Comments
 (0)