Index: clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -11,9 +11,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ComputeDependence.h" +#include "clang/AST/Decl.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -27,8 +31,7 @@ namespace { class StackAddrEscapeChecker - : public Checker, - check::EndFunction> { + : public Checker, check::EndFunction> { mutable IdentifierInfo *dispatch_semaphore_tII; mutable std::unique_ptr BT_stackleak; mutable std::unique_ptr BT_returnstack; @@ -46,10 +49,15 @@ CheckerNameRef CheckNames[CK_NumCheckKinds]; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; - void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; + void checkPreReturnStmt(const ReturnStmt *RS, CheckerContext &C) const; + void checkPreStmt(const Stmt *S, CheckerContext &C) const; void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const; private: + void checkAssignmentStmtForStackObjEscapeToPtrOutParam( + const BinaryOperator *Assignment, CheckerContext &C) const; + void checkAssignmentStmtForStackArrayEscapeToPtrOutParam( + const BinaryOperator *Assignment, CheckerContext &C) const; void checkReturnedBlockCaptures(const BlockDataRegion &B, CheckerContext &C) const; void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B, @@ -249,8 +257,8 @@ } } -void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, - CheckerContext &C) const { +void StackAddrEscapeChecker::checkPreReturnStmt(const ReturnStmt *RS, + CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrEscapeChecker]) return; @@ -288,9 +296,55 @@ } } + // TODO how about returning a global variable? EmitStackError(C, R, RetE); } +void StackAddrEscapeChecker::checkAssignmentStmtForStackObjEscapeToPtrOutParam( + const BinaryOperator *Assignment, CheckerContext &C) const { + { + Expr* LHS = Assignment->getLHS(); + SVal LHSSVal = C.getSVal(LHS); + const MemRegion *LHSMemReg = LHSSVal.getAsRegion(); + // We bail if we can't tell which memory region backs LHS. + if (!LHSMemReg) + return; + + auto LHSMemSpace = LHSMemReg->getMemorySpace(); + // There's no direct escape if we are assigning to memory in the current stack frame. + if (isa(LHSMemSpace) && !isNotInCurrentFrame(LHSMemReg, C)) + return; + } + + Expr* RHS = Assignment->getRHS(); + SVal RHSSVal = C.getSVal(RHS); + const MemRegion *RHSMemReg = RHSSVal.getAsRegion(); + if (!RHSMemReg) + return; + + auto RHSMemSpace = RHSMemReg->getMemorySpace(); + if (isa(RHSMemSpace) && !isNotInCurrentFrame(RHSMemReg, C)) + EmitStackError(C, RHSMemReg, RHS); +} + +void StackAddrEscapeChecker::checkPreStmt(const Stmt *S, + CheckerContext &C) const { + if (isa(S)) { + checkPreReturnStmt(cast(S), C); + return; + } + if (!ChecksEnabled[CK_StackAddrEscapeChecker]) + return; + + const BinaryOperator *BO = dyn_cast_or_null(S); + if (!BO) + return; + + if (!BO->isAssignmentOp()) + return; + checkAssignmentStmtForStackObjEscapeToPtrOutParam(BO, C); +} + void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const { if (!ChecksEnabled[CK_StackAddrEscapeChecker]) Index: clang/test/Analysis/Checkers/StackAddrEscapeChecker.c =================================================================== --- /dev/null +++ clang/test/Analysis/Checkers/StackAddrEscapeChecker.c @@ -0,0 +1,100 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core.StackAddressEscape -verify %s + +void leaks_stack_obj_to_outparam(int **outparam) { + int stack_allocated_obj = 42; + *outparam = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + +void leaks_stack_obj_to_outparam_2(int **outparam) { + int stack_allocated_obj = 42; + outparam[0] = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + +void leaks_stack_array_to_outparam(int **outparam) { + int stack_allocated_array[1]; + stack_allocated_array[0] = 42; + *outparam = stack_allocated_array; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_array' returned to caller [core.StackAddressEscape]}} +} + +void leaks_stack_obj_to_outparam_array(int *outparam[1]) { + int stack_allocated_obj = 42; + outparam[0] = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + +void leaks_stack_obj_to_outparam_many_unop_LHS(int **outparam) { + int stack_allocated_array = 42; + *&*outparam = &stack_allocated_array; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_array' returned to caller [core.StackAddressEscape]}} +} + +void leaks_stack_obj_to_outparam_many_unop_RHS(int **outparam) { + int stack_allocated_array = 42; + *outparam = &*&stack_allocated_array; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_array' returned to caller [core.StackAddressEscape]}} +} + +struct foo_s { + int *stuff; +}; + +void leaks_stack_obj_to_outparam_struct(struct foo_s *outparam) { + int stack_allocated_obj = 42; + outparam->stuff = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + +void leaks_call_arg_to_outparam(int param, int **outparam) { + *outparam = ¶m; // expected-warning{{Address of stack memory associated with local variable 'param' returned to caller [core.StackAddressEscape]}} +} + +void dont_leak_int(void) { + int ** local_ptr_ptr; + int local_var = 42; + // This is OK as nothing escapes the stack frame. + *local_ptr_ptr = &local_var; +} + +void dont_leak_struct(void) { + struct foo_s local_struct; + int local_var = 42; + // This is OK as nothing escapes the stack frame. + local_struct.stuff = &local_var; +} + +void assigns_param_to_param(int** outparam, int* inparam) { + // We don't know the lifetime of inparam - the caller is managing it. + *outparam = inparam; +} + +void assigns_param_to_param_structs(struct foo_s* outparam, struct foo_s* inparam) { + // We don't know the lifetime of inparam - the caller is managing it. + outparam->stuff = inparam->stuff; +} + +void leak_stack_addr_to_outparam_indirectly(int ** outparam) { + int local_var = 42; + int* ptr = &local_var; + *outparam = ptr; // expected-warning{{Address of stack memory associated with local variable 'local_var' returned to caller [core.StackAddressEscape]}} +} + +void assigns_null_to_outparam(int ** outparam) { + int* local_var = /* NULL */ 0; + *outparam = local_var; +} + +typedef unsigned long size_t; +void* malloc(size_t size); +void assigns_heap_addr_to_outparam(int ** outparam) { + int* heap_var = malloc(sizeof(int)); + *outparam = heap_var; +} + +int** return_random_ptr_ptr(void); +void leaks_stack_obj_to_function_return_value(struct foo_s *outparam) { + int stack_allocated_obj = 42; + *(return_random_ptr_ptr()) = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + +int* global_var; +void leaks_stack_obj_to_global_var() { + int stack_allocated_obj = 42; + global_var = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + Index: clang/test/Analysis/Checkers/StackAddrEscapeChecker.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/Checkers/StackAddrEscapeChecker.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core.StackAddressEscape -verify %s + +void leaks_stack_obj_to_outparam(unsigned long& outparam) { + int stack_allocated_obj = 42; + outparam = (unsigned long) &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + +void dont_leak_stack_obj_to_outparam(int& outparam) { + int stack_allocated_obj = 42; + outparam = stack_allocated_obj; +} + +void leaks_stack_obj_to_outparam_2(int*& outparam) { + int stack_allocated_obj = 42; + outparam = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} + +struct foo_s { + int *stuff; +}; + +void leaks_stack_obj_to_outparam_struct(foo_s& outparam) { + int stack_allocated_obj = 42; + outparam.stuff = &stack_allocated_obj; // expected-warning{{Address of stack memory associated with local variable 'stack_allocated_obj' returned to caller [core.StackAddressEscape]}} +} Index: clang/test/Analysis/loop-block-counts.c =================================================================== --- clang/test/Analysis/loop-block-counts.c +++ clang/test/Analysis/loop-block-counts.c @@ -4,7 +4,7 @@ void callee(void **p) { int x; - *p = &x; + *p = &x; // // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller [core.StackAddressEscape]}} } void loop() {