aboutsummaryrefslogtreecommitdiff
path: root/lib/Checker/StackAddrLeakChecker.cpp
blob: 0bd9e02448661781b63274fe6af33e9e54ede1c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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);
              }
            }
          }            
        }
      }
    }
  }
}