Skip to content

Commit 2d7ad98

Browse files
pskrgagtstellar
authored andcommitted
[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 (cherry picked from commit 060f955)
1 parent e7ae553 commit 2d7ad98

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
@@ -97,10 +97,14 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
9797
void handleOverflowBuiltin(const CallEvent &Call, CheckerContext &C,
9898
BinaryOperator::Opcode Op,
9999
QualType ResultType) const;
100-
const NoteTag *createBuiltinNoOverflowNoteTag(CheckerContext &C,
101-
bool BothFeasible, SVal Arg1,
102-
SVal Arg2, SVal Result) const;
103-
const NoteTag *createBuiltinOverflowNoteTag(CheckerContext &C) const;
100+
const NoteTag *createBuiltinOverflowNoteTag(CheckerContext &C,
101+
bool BothFeasible, SVal Arg1,
102+
SVal Arg2, SVal Result) const;
103+
ProgramStateRef initStateAftetBuiltinOverflow(CheckerContext &C,
104+
ProgramStateRef State,
105+
const CallEvent &Call,
106+
SVal RetCal,
107+
bool IsOverflow) const;
104108
std::pair<bool, bool> checkOverflow(CheckerContext &C, SVal RetVal,
105109
QualType Res) const;
106110

@@ -122,30 +126,24 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
122126

123127
} // namespace
124128

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

133-
// Propagate interestingness to input argumets if result is interesting.
136+
// Propagate interestingness to input arguments if result is interesting.
134137
BR.markInteresting(Arg1);
135138
BR.markInteresting(Arg2);
136139

137-
if (BothFeasible)
140+
if (overflow)
141+
OS << "Assuming overflow";
142+
else
138143
OS << "Assuming no overflow";
139144
});
140145
}
141146

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

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

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

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

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

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

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

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

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 garbage or undefined}}
27-
// expected-note@-1 {{Assigned value is garbage or undefined}}
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)