llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-static-analyzer-1 Author: Pavel Skripkin (pskrgag) <details> <summary>Changes</summary> Add basic support for `builtin_*_overflow` primitives. These helps a lot for checking custom calloc-like functions with inlinable body. Without such support code like ```c #include <stddef.h> #include <stdlib.h> static void *myMalloc(size_t a1, size_t a2) { size_t res; if (__builtin_smul_overflow(a1, a2, &res)) return NULL; return malloc(res); } void test(void) { char *ptr = myMalloc(10, 1); ptr[20] = 10; } ```` does not trigger any warnings. --- Full diff: https://github.com/llvm/llvm-project/pull/102602.diff 3 Files Affected: - (modified) clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp (+122-2) - (added) clang/test/Analysis/builtin_overflow.c (+65) - (modified) clang/test/Analysis/out-of-bounds-diagnostics.c (+17) ``````````diff diff --git a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index b198b1c2ff4d11..0c8b9fa3d947b0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -21,16 +21,67 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" using namespace clang; using namespace ento; namespace { +QualType getOverflowBuiltinResultType(const CallEvent &Call) { + assert(Call.getNumArgs() == 3); + + return Call.getArgExpr(2)->getType()->getPointeeType(); +} + +QualType getOverflowBuiltinResultType(const CallEvent &Call, CheckerContext &C, + unsigned BI) { + assert(Call.getNumArgs() == 3); + + ASTContext &Ast = C.getASTContext(); + + switch (BI) { + case Builtin::BI__builtin_smul_overflow: + case Builtin::BI__builtin_ssub_overflow: + case Builtin::BI__builtin_sadd_overflow: + return Ast.IntTy; + case Builtin::BI__builtin_smull_overflow: + case Builtin::BI__builtin_ssubl_overflow: + case Builtin::BI__builtin_saddl_overflow: + return Ast.LongTy; + case Builtin::BI__builtin_smulll_overflow: + case Builtin::BI__builtin_ssubll_overflow: + case Builtin::BI__builtin_saddll_overflow: + return Ast.LongLongTy; + case Builtin::BI__builtin_umul_overflow: + case Builtin::BI__builtin_usub_overflow: + case Builtin::BI__builtin_uadd_overflow: + return Ast.UnsignedIntTy; + case Builtin::BI__builtin_umull_overflow: + case Builtin::BI__builtin_usubl_overflow: + case Builtin::BI__builtin_uaddl_overflow: + return Ast.UnsignedLongTy; + case Builtin::BI__builtin_umulll_overflow: + case Builtin::BI__builtin_usubll_overflow: + case Builtin::BI__builtin_uaddll_overflow: + return Ast.UnsignedLongLongTy; + case Builtin::BI__builtin_mul_overflow: + case Builtin::BI__builtin_sub_overflow: + case Builtin::BI__builtin_add_overflow: + return getOverflowBuiltinResultType(Call); + default: + assert(false && "Unknown overflow builtin"); + } +} + class BuiltinFunctionChecker : public Checker<eval::Call> { public: bool evalCall(const CallEvent &Call, CheckerContext &C) const; + void HandleOverflowBuiltin(const CallEvent &Call, CheckerContext &C, + BinaryOperator::Opcode Op, + QualType ResultType) const; private: // From: clang/include/clang/Basic/Builtins.def @@ -50,6 +101,44 @@ class BuiltinFunctionChecker : public Checker<eval::Call> { } // namespace +void BuiltinFunctionChecker::HandleOverflowBuiltin(const CallEvent &Call, + CheckerContext &C, + BinaryOperator::Opcode Op, + QualType ResultType) const { + // All __builtin_*_overflow functions take 3 argumets. + assert(Call.getNumArgs() == 3); + + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + const Expr *CE = Call.getOriginExpr(); + + SVal Arg1 = Call.getArgSVal(0); + SVal Arg2 = Call.getArgSVal(1); + + SVal RetVal = SVB.evalBinOp(State, Op, Arg1, Arg2, ResultType); + + // TODO: Handle overflows with values that known to overflow. Like INT_MAX + 1 + // should not produce state for non-overflow case and threat it as + // unreachable. + + // Handle non-overflow case. + { + ProgramStateRef StateSuccess = + State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(false)); + + if (auto L = Call.getArgSVal(2).getAs<Loc>()) + StateSuccess = StateSuccess->bindLoc(*L, RetVal, C.getLocationContext()); + + C.addTransition(StateSuccess); + } + + // Handle overflow case. + { + C.addTransition( + State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(true))); + } +} + bool BuiltinFunctionChecker::isBuiltinLikeFunction( const CallEvent &Call) const { const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Call.getDecl()); @@ -82,10 +171,41 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, return true; } - switch (FD->getBuiltinID()) { + unsigned BI = FD->getBuiltinID(); + + switch (BI) { default: return false; - + case Builtin::BI__builtin_mul_overflow: + case Builtin::BI__builtin_smul_overflow: + case Builtin::BI__builtin_smull_overflow: + case Builtin::BI__builtin_smulll_overflow: + case Builtin::BI__builtin_umul_overflow: + case Builtin::BI__builtin_umull_overflow: + case Builtin::BI__builtin_umulll_overflow: + HandleOverflowBuiltin(Call, C, BO_Mul, + getOverflowBuiltinResultType(Call, C, BI)); + return true; + case Builtin::BI__builtin_sub_overflow: + case Builtin::BI__builtin_ssub_overflow: + case Builtin::BI__builtin_ssubl_overflow: + case Builtin::BI__builtin_ssubll_overflow: + case Builtin::BI__builtin_usub_overflow: + case Builtin::BI__builtin_usubl_overflow: + case Builtin::BI__builtin_usubll_overflow: + HandleOverflowBuiltin(Call, C, BO_Sub, + getOverflowBuiltinResultType(Call, C, BI)); + return true; + case Builtin::BI__builtin_add_overflow: + case Builtin::BI__builtin_sadd_overflow: + case Builtin::BI__builtin_saddl_overflow: + case Builtin::BI__builtin_saddll_overflow: + case Builtin::BI__builtin_uadd_overflow: + case Builtin::BI__builtin_uaddl_overflow: + case Builtin::BI__builtin_uaddll_overflow: + HandleOverflowBuiltin(Call, C, BO_Add, + getOverflowBuiltinResultType(Call, C, BI)); + return true; case Builtin::BI__builtin_assume: case Builtin::BI__assume: { assert (Call.getNumArgs() > 0); diff --git a/clang/test/Analysis/builtin_overflow.c b/clang/test/Analysis/builtin_overflow.c new file mode 100644 index 00000000000000..3b8639aa450046 --- /dev/null +++ b/clang/test/Analysis/builtin_overflow.c @@ -0,0 +1,65 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-unknown -verify %s \ +// RUN: -analyzer-checker=core,debug.ExprInspection + +#define NULL ((void *)0) +#define INT_MAX __INT_MAX__ + +void clang_analyzer_dump_int(int); + +void test1(void) +{ + int res; + + if (__builtin_add_overflow(10, 20, &res)) { + clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} + return; + } + + clang_analyzer_dump_int(res); //expected-warning{{30}} +} + +void test2(void) +{ + int res; + + __builtin_add_overflow(10, 20, &res); + clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} expected-warning{{S32b}} +} + +void test3(void) +{ + int res; + + if (__builtin_sub_overflow(10, 20, &res)) { + clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} + return; + } + + clang_analyzer_dump_int(res); //expected-warning{{-10}} +} + +void test4(void) +{ + int res; + + if (__builtin_sub_overflow(10, 20, &res)) { + clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} + return; + } + + if (res != -10) { + *(volatile char *)NULL; //no warning + } +} + +void test5(void) +{ + int res; + + if (__builtin_mul_overflow(10, 20, &res)) { + clang_analyzer_dump_int(res); //expected-warning{{1st function call argument is an uninitialized value}} + return; + } + + clang_analyzer_dump_int(res); //expected-warning{{200}} +} diff --git a/clang/test/Analysis/out-of-bounds-diagnostics.c b/clang/test/Analysis/out-of-bounds-diagnostics.c index de70e483add1c0..73b56e7a8ba4db 100644 --- a/clang/test/Analysis/out-of-bounds-diagnostics.c +++ b/clang/test/Analysis/out-of-bounds-diagnostics.c @@ -278,6 +278,23 @@ int *mallocRegion(void) { return mem; } +int *custom_calloc(size_t a, size_t b) { + size_t res; + if (__builtin_mul_overflow(a, b, &res)) + return 0; + + return malloc(res); +} + +int *mallocRegionOverflow(void) { + int *mem = (int*)custom_calloc(4, 10); + + mem[20] = 10; + // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}} + // expected-note@-2 {{Access of the heap area at index 20, while it holds only 10 'int' elements}} + return mem; +} + int *mallocRegionDeref(void) { int *mem = (int*)malloc(2*sizeof(int)); `````````` </details> https://github.com/llvm/llvm-project/pull/102602 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits