[clang] 060f955 - [clang][analyzer] Fix error path of builtin overflow (#136345)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Apr 20 07:14:45 PDT 2025
Author: Pavel Skripkin
Date: 2025-04-20T16:14:41+02:00
New Revision: 060f9556a2f6ef4669f1c2cd8c4a4d76748a440f
URL: https://github.com/llvm/llvm-project/commit/060f9556a2f6ef4669f1c2cd8c4a4d76748a440f
DIFF: https://github.com/llvm/llvm-project/commit/060f9556a2f6ef4669f1c2cd8c4a4d76748a440f.diff
LOG: [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
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
clang/test/Analysis/builtin_overflow.c
clang/test/Analysis/builtin_overflow_notes.c
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
index b1a11442dec53..8eb68b1fa638e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -96,10 +96,14 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
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;
@@ -121,30 +125,24 @@ class BuiltinFunctionChecker : public Checker<eval::Call> {
} // 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 {
@@ -174,6 +172,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,
@@ -183,8 +204,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);
@@ -194,29 +213,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));
-
- 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);
- }
+ if (NotOverflow) {
+ auto NewState =
+ initStateAftetBuiltinOverflow(C, State, Call, RetVal, false);
- 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));
}
}
diff --git a/clang/test/Analysis/builtin_overflow.c b/clang/test/Analysis/builtin_overflow.c
index 9d98ce7a1af45..d290333071dc9 100644
--- a/clang/test/Analysis/builtin_overflow.c
+++ b/clang/test/Analysis/builtin_overflow.c
@@ -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
}
diff --git a/clang/test/Analysis/builtin_overflow_notes.c b/clang/test/Analysis/builtin_overflow_notes.c
index 5fa2156df925c..94c79b5ed334a 100644
--- a/clang/test/Analysis/builtin_overflow_notes.c
+++ b/clang/test/Analysis/builtin_overflow_notes.c
@@ -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 at -1 {{Taking true branch}}
- int var = res; // expected-warning{{Assigned value is uninitialized}}
- // expected-note at -1 {{Assigned value is uninitialized}}
+ if (res) { // expected-note {{Assuming 'res' is not equal to 0}}
+ // expected-note at -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 at -1 {{Dereference of null pointer}}
+ }
return;
}
}
More information about the cfe-commits
mailing list