diff options
-rw-r--r-- | lib/Checker/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Checker/GRExprEngineExperimentalChecks.cpp | 1 | ||||
-rw-r--r-- | lib/Checker/GRExprEngineInternalChecks.h | 1 | ||||
-rw-r--r-- | lib/Checker/StackAddrLeakChecker.cpp | 94 | ||||
-rw-r--r-- | test/Analysis/stackaddrleak.c | 14 |
5 files changed, 111 insertions, 0 deletions
diff --git a/lib/Checker/CMakeLists.txt b/lib/Checker/CMakeLists.txt index 4e7c8b856c..865d7a1226 100644 --- a/lib/Checker/CMakeLists.txt +++ b/lib/Checker/CMakeLists.txt @@ -57,6 +57,7 @@ add_clang_library(clangChecker ReturnUndefChecker.cpp SimpleConstraintManager.cpp SimpleSValuator.cpp + StackAddrLeakChecker.cpp Store.cpp SVals.cpp SValuator.cpp diff --git a/lib/Checker/GRExprEngineExperimentalChecks.cpp b/lib/Checker/GRExprEngineExperimentalChecks.cpp index 6066a1c74d..6c9252bcd9 100644 --- a/lib/Checker/GRExprEngineExperimentalChecks.cpp +++ b/lib/Checker/GRExprEngineExperimentalChecks.cpp @@ -38,4 +38,5 @@ void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { RegisterCastToStructChecker(Eng); RegisterCastSizeChecker(Eng); RegisterArrayBoundChecker(Eng); + RegisterStackAddrLeakChecker(Eng); } diff --git a/lib/Checker/GRExprEngineInternalChecks.h b/lib/Checker/GRExprEngineInternalChecks.h index 335b85e308..f86e53b4bd 100644 --- a/lib/Checker/GRExprEngineInternalChecks.h +++ b/lib/Checker/GRExprEngineInternalChecks.h @@ -36,6 +36,7 @@ void RegisterPointerSubChecker(GRExprEngine &Eng); void RegisterReturnPointerRangeChecker(GRExprEngine &Eng); void RegisterReturnStackAddressChecker(GRExprEngine &Eng); void RegisterReturnUndefChecker(GRExprEngine &Eng); +void RegisterStackAddrLeakChecker(GRExprEngine &Eng); void RegisterUndefBranchChecker(GRExprEngine &Eng); void RegisterUndefCapturedBlockVarChecker(GRExprEngine &Eng); void RegisterUndefResultChecker(GRExprEngine &Eng); diff --git a/lib/Checker/StackAddrLeakChecker.cpp b/lib/Checker/StackAddrLeakChecker.cpp new file mode 100644 index 0000000000..0bd9e02448 --- /dev/null +++ b/lib/Checker/StackAddrLeakChecker.cpp @@ -0,0 +1,94 @@ +//=== StackAddrLeakChecker.cpp ------------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines stack address leak checker, which checks if an invalid +// stack address is stored into a global or heap location. See CERT DCL30-C. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Checker/BugReporter/BugType.h" +#include "clang/Checker/PathSensitive/Checker.h" +#include "clang/Checker/PathSensitive/GRState.h" + +using namespace clang; + +namespace { +class StackAddrLeakChecker : public Checker { + BuiltinBug *BT_stackleak; + +public: + StackAddrLeakChecker() : BT_stackleak(0) {} + static void *getTag() { + static int x; + return &x; + } + + void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); +}; +} + +void clang::RegisterStackAddrLeakChecker(GRExprEngine &Eng) { + Eng.registerCheck(new StackAddrLeakChecker()); +} + +void StackAddrLeakChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, + GRExprEngine &Eng) { + SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode); + const GRState *state = B.getState(); + TranslationUnitDecl *TU = Eng.getContext().getTranslationUnitDecl(); + + // Check each global variable if it contains a MemRegionVal of a stack + // variable declared in the function we are leaving. + for (DeclContext::decl_iterator I = TU->decls_begin(), E = TU->decls_end(); + I != E; ++I) { + if (VarDecl *VD = dyn_cast<VarDecl>(*I)) { + const LocationContext *LCtx = B.getPredecessor()->getLocationContext(); + SVal L = state->getLValue(VD, LCtx); + SVal V = state->getSVal(cast<Loc>(L)); + if (loc::MemRegionVal *RV = dyn_cast<loc::MemRegionVal>(&V)) { + const MemRegion *R = RV->getRegion(); + // Strip fields or elements to get the variable region. + R = R->getBaseRegion(); + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + const VarDecl *VD = VR->getDecl(); + const DeclContext *DC = VD->getDeclContext(); + // Get the function where the variable is declared. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC)) { + // Check if the function is the function we are leaving. + if (FD == LCtx->getDecl()) { + // The variable is declared in the function scope which we are + // leaving. Keeping this variable's address in a global variable + // is dangerous. + // FIXME: Currently VarRegion does not carry context information. + // So we cannot tell if the local variable instance is in the + // current stack frame. This may produce false positive in + // recursive function call context. But that's a rare case. + + // FIXME: better warning location. + + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_stackleak) + BT_stackleak = new BuiltinBug("Stack address leak", + "Stack address was saved into a global variable. " + "This is dangerous because the address will become invalid " + "after returning from the function."); + BugReport *R = new BugReport(*BT_stackleak, + BT_stackleak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } + } + } + } + } +} + diff --git a/test/Analysis/stackaddrleak.c b/test/Analysis/stackaddrleak.c new file mode 100644 index 0000000000..0076b27a3c --- /dev/null +++ b/test/Analysis/stackaddrleak.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store region -analyzer-experimental-internal-checks -verify %s + +char const *p; + +void f0() { + char const str[] = "This will change"; + p = str; // expected-warning {{Stack address was saved into a global variable.}} +} + +void f1() { + char const str[] = "This will change"; + p = str; + p = 0; // no-warning +} |