[clang][analyzer] Fix error path of builtin overflow (#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 #136292 (cherry picked from commit 060f9556a2f6ef4669f1c2cd8c4a4d76748a440f)
This commit is contained in:
parent
e7ae5532bc
commit
2d7ad98ec0
@ -97,10 +97,14 @@ public:
|
||||
void handleOverflowBuiltin(const CallEvent &Call, CheckerContext &C,
|
||||
BinaryOperator::Opcode Op,
|
||||
QualType ResultType) const;
|
||||
const NoteTag *createBuiltinNoOverflowNoteTag(CheckerContext &C,
|
||||
bool BothFeasible, SVal Arg1,
|
||||
SVal Arg2, SVal Result) const;
|
||||
const NoteTag *createBuiltinOverflowNoteTag(CheckerContext &C) const;
|
||||
const NoteTag *createBuiltinOverflowNoteTag(CheckerContext &C,
|
||||
bool BothFeasible, SVal Arg1,
|
||||
SVal Arg2, SVal Result) const;
|
||||
ProgramStateRef initStateAftetBuiltinOverflow(CheckerContext &C,
|
||||
ProgramStateRef State,
|
||||
const CallEvent &Call,
|
||||
SVal RetCal,
|
||||
bool IsOverflow) const;
|
||||
std::pair<bool, bool> checkOverflow(CheckerContext &C, SVal RetVal,
|
||||
QualType Res) const;
|
||||
|
||||
@ -122,30 +126,24 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
const NoteTag *BuiltinFunctionChecker::createBuiltinNoOverflowNoteTag(
|
||||
CheckerContext &C, bool BothFeasible, SVal Arg1, SVal Arg2,
|
||||
SVal Result) const {
|
||||
return C.getNoteTag([Result, Arg1, Arg2, BothFeasible](
|
||||
PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
|
||||
const NoteTag *BuiltinFunctionChecker::createBuiltinOverflowNoteTag(
|
||||
CheckerContext &C, bool overflow, SVal Arg1, SVal Arg2, SVal Result) const {
|
||||
return C.getNoteTag([Result, Arg1, Arg2, overflow](PathSensitiveBugReport &BR,
|
||||
llvm::raw_ostream &OS) {
|
||||
if (!BR.isInteresting(Result))
|
||||
return;
|
||||
|
||||
// Propagate interestingness to input argumets if result is interesting.
|
||||
// Propagate interestingness to input arguments if result is interesting.
|
||||
BR.markInteresting(Arg1);
|
||||
BR.markInteresting(Arg2);
|
||||
|
||||
if (BothFeasible)
|
||||
if (overflow)
|
||||
OS << "Assuming overflow";
|
||||
else
|
||||
OS << "Assuming no overflow";
|
||||
});
|
||||
}
|
||||
|
||||
const NoteTag *
|
||||
BuiltinFunctionChecker::createBuiltinOverflowNoteTag(CheckerContext &C) const {
|
||||
return C.getNoteTag([](PathSensitiveBugReport &BR,
|
||||
llvm::raw_ostream &OS) { OS << "Assuming overflow"; },
|
||||
/*isPrunable=*/true);
|
||||
}
|
||||
|
||||
std::pair<bool, bool>
|
||||
BuiltinFunctionChecker::checkOverflow(CheckerContext &C, SVal RetVal,
|
||||
QualType Res) const {
|
||||
@ -175,6 +173,29 @@ BuiltinFunctionChecker::checkOverflow(CheckerContext &C, SVal RetVal,
|
||||
return {MayOverflow || MayUnderflow, MayNotOverflow && MayNotUnderflow};
|
||||
}
|
||||
|
||||
ProgramStateRef BuiltinFunctionChecker::initStateAftetBuiltinOverflow(
|
||||
CheckerContext &C, ProgramStateRef State, const CallEvent &Call,
|
||||
SVal RetVal, bool IsOverflow) const {
|
||||
SValBuilder &SVB = C.getSValBuilder();
|
||||
SVal Arg1 = Call.getArgSVal(0);
|
||||
SVal Arg2 = Call.getArgSVal(1);
|
||||
auto BoolTy = C.getASTContext().BoolTy;
|
||||
|
||||
ProgramStateRef NewState =
|
||||
State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
|
||||
SVB.makeTruthVal(IsOverflow, BoolTy));
|
||||
|
||||
if (auto L = Call.getArgSVal(2).getAs<Loc>()) {
|
||||
NewState = NewState->bindLoc(*L, RetVal, C.getLocationContext());
|
||||
|
||||
// Propagate taint if any of the arguments were tainted
|
||||
if (isTainted(State, Arg1) || isTainted(State, Arg2))
|
||||
NewState = addTaint(NewState, *L);
|
||||
}
|
||||
|
||||
return NewState;
|
||||
}
|
||||
|
||||
void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call,
|
||||
CheckerContext &C,
|
||||
BinaryOperator::Opcode Op,
|
||||
@ -184,8 +205,6 @@ void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call,
|
||||
|
||||
ProgramStateRef State = C.getState();
|
||||
SValBuilder &SVB = C.getSValBuilder();
|
||||
const Expr *CE = Call.getOriginExpr();
|
||||
auto BoolTy = C.getASTContext().BoolTy;
|
||||
|
||||
SVal Arg1 = Call.getArgSVal(0);
|
||||
SVal Arg2 = Call.getArgSVal(1);
|
||||
@ -195,29 +214,20 @@ void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call,
|
||||
SVal RetVal = SVB.evalBinOp(State, Op, Arg1, Arg2, ResultType);
|
||||
|
||||
auto [Overflow, NotOverflow] = checkOverflow(C, RetValMax, ResultType);
|
||||
|
||||
if (NotOverflow) {
|
||||
ProgramStateRef StateNoOverflow = State->BindExpr(
|
||||
CE, C.getLocationContext(), SVB.makeTruthVal(false, BoolTy));
|
||||
auto NewState =
|
||||
initStateAftetBuiltinOverflow(C, State, Call, RetVal, false);
|
||||
|
||||
if (auto L = Call.getArgSVal(2).getAs<Loc>()) {
|
||||
StateNoOverflow =
|
||||
StateNoOverflow->bindLoc(*L, RetVal, C.getLocationContext());
|
||||
|
||||
// Propagate taint if any of the argumets were tainted
|
||||
if (isTainted(State, Arg1) || isTainted(State, Arg2))
|
||||
StateNoOverflow = addTaint(StateNoOverflow, *L);
|
||||
}
|
||||
|
||||
C.addTransition(
|
||||
StateNoOverflow,
|
||||
createBuiltinNoOverflowNoteTag(
|
||||
C, /*BothFeasible=*/NotOverflow && Overflow, Arg1, Arg2, RetVal));
|
||||
C.addTransition(NewState, createBuiltinOverflowNoteTag(
|
||||
C, /*overflow=*/false, Arg1, Arg2, RetVal));
|
||||
}
|
||||
|
||||
if (Overflow) {
|
||||
C.addTransition(State->BindExpr(CE, C.getLocationContext(),
|
||||
SVB.makeTruthVal(true, BoolTy)),
|
||||
createBuiltinOverflowNoteTag(C));
|
||||
auto NewState = initStateAftetBuiltinOverflow(C, State, Call, RetVal, true);
|
||||
|
||||
C.addTransition(NewState, createBuiltinOverflowNoteTag(C, /*overflow=*/true,
|
||||
Arg1, Arg2, RetVal));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ void test_add_overflow(void)
|
||||
int res;
|
||||
|
||||
if (__builtin_add_overflow(__INT_MAX__, 1, &res)) {
|
||||
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
|
||||
clang_analyzer_dump_int(res); //expected-warning{{-2147483648 S32b}}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ void test_add_underoverflow(void)
|
||||
int res;
|
||||
|
||||
if (__builtin_add_overflow(__INT_MIN__, -1, &res)) {
|
||||
clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}}
|
||||
clang_analyzer_dump_int(res); //expected-warning{{2147483647 S32b}}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ void test_bool_assign(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
// Reproduce issue from GH#111147. __builtin_*_overflow funcions
|
||||
// Reproduce issue from GH#111147. __builtin_*_overflow functions
|
||||
// should return _Bool, but not int.
|
||||
_Bool ret = __builtin_mul_overflow(10, 20, &res); // no crash
|
||||
}
|
||||
|
@ -19,12 +19,16 @@ void test_no_overflow_note(int a, int b)
|
||||
|
||||
void test_overflow_note(int a, int b)
|
||||
{
|
||||
int res; // expected-note{{'res' declared without an initial value}}
|
||||
int res;
|
||||
|
||||
if (__builtin_add_overflow(a, b, &res)) { // expected-note {{Assuming overflow}}
|
||||
// expected-note@-1 {{Taking true branch}}
|
||||
int var = res; // expected-warning{{Assigned value is garbage or undefined}}
|
||||
// expected-note@-1 {{Assigned value is garbage or undefined}}
|
||||
if (res) { // expected-note {{Assuming 'res' is not equal to 0}}
|
||||
// expected-note@-1 {{Taking true branch}}
|
||||
int *ptr = 0; // expected-note {{'ptr' initialized to a null pointer value}}
|
||||
int var = *(int *) ptr; //expected-warning {{Dereference of null pointer}}
|
||||
//expected-note@-1 {{Dereference of null pointer}}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user