]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/Checker/PthreadLockChecker.cpp
Update clang to r94309.
[FreeBSD/FreeBSD.git] / lib / Checker / PthreadLockChecker.cpp
1 //===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This defines PthreadLockChecker, a simple lock -> unlock checker.  Eventually
11 // this shouldn't be registered with GRExprEngineInternalChecks.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "clang/Checker/PathSensitive/CheckerVisitor.h"
16 #include "clang/Checker/BugReporter/BugReporter.h"
17 #include "clang/Checker/PathSensitive/GRStateTrait.h"
18 #include "GRExprEngineExperimentalChecks.h"
19 #include "llvm/ADT/ImmutableSet.h"
20
21 using namespace clang;
22
23 namespace {
24 class PthreadLockChecker
25   : public CheckerVisitor<PthreadLockChecker> {
26   BugType *BT;
27 public:
28   PthreadLockChecker() : BT(0) {}
29   static void *getTag() {
30     static int x = 0;
31     return &x;
32   }
33   void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE);
34     
35   void AcquireLock(CheckerContext &C, const CallExpr *CE,
36                    SVal lock, bool isTryLock);
37     
38   void ReleaseLock(CheckerContext &C, const CallExpr *CE,
39                     SVal lock);
40
41 };
42 } // end anonymous namespace
43
44 // GDM Entry for tracking lock state.
45 namespace { class LockSet {}; }
46 namespace clang {
47 template <> struct GRStateTrait<LockSet> :
48   public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
49     static void* GDMIndex() { return PthreadLockChecker::getTag(); }
50 };
51 } // end clang namespace
52
53 void clang::RegisterPthreadLockChecker(GRExprEngine &Eng) {
54   Eng.registerCheck(new PthreadLockChecker());
55 }
56
57
58 void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C,
59                                            const CallExpr *CE) {
60   const GRState *state = C.getState();
61   const Expr *Callee = CE->getCallee();
62   const FunctionTextRegion *R =
63     dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
64   
65   if (!R)
66     return;
67   
68   llvm::StringRef FName = R->getDecl()->getName();
69   
70   if (FName == "pthread_mutex_lock") {
71     if (CE->getNumArgs() != 1)
72       return;
73     AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false);
74   }
75   else if (FName == "pthread_mutex_trylock") {
76     if (CE->getNumArgs() != 1)
77       return;
78     AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true);
79   }  
80   else if (FName == "pthread_mutex_unlock") {
81     if (CE->getNumArgs() != 1)
82       return;
83     ReleaseLock(C, CE, state->getSVal(CE->getArg(0)));
84   }
85 }
86
87 void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
88                                      SVal lock, bool isTryLock) {
89   
90   const MemRegion *lockR = lock.getAsRegion();
91   if (!lockR)
92     return;
93   
94   const GRState *state = C.getState();
95   
96   SVal X = state->getSVal(CE);
97   if (X.isUnknownOrUndef())
98     return;
99   
100   DefinedSVal retVal = cast<DefinedSVal>(X);
101   const GRState *lockSucc = state;
102   
103   if (isTryLock) {
104       // Bifurcate the state, and allow a mode where the lock acquisition fails.
105     const GRState *lockFail;
106     llvm::tie(lockFail, lockSucc) = state->Assume(retVal);    
107     assert(lockFail && lockSucc);
108     C.addTransition(C.GenerateNode(CE, lockFail));
109   }
110   else {
111       // Assume that the return value was 0.
112     lockSucc = state->Assume(retVal, false);
113     assert(lockSucc);
114   }
115   
116     // Record that the lock was acquired.  
117   lockSucc = lockSucc->add<LockSet>(lockR);
118   
119   C.addTransition(lockSucc != state ? C.GenerateNode(CE, lockSucc) :
120                   C.getPredecessor());
121 }
122
123 void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
124                                      SVal lock) {
125
126   const MemRegion *lockR = lock.getAsRegion();
127   if (!lockR)
128     return;
129   
130   const GRState *state = C.getState();
131
132   // Record that the lock was released.  
133   // FIXME: Handle unlocking locks that were never acquired.  This may
134   // require IPA for wrappers.
135   const GRState *unlockState = state->remove<LockSet>(lockR);
136   
137   if (state == unlockState)
138     return;
139   
140   C.addTransition(C.GenerateNode(CE, unlockState));  
141 }