xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp (revision 507ff53e39e345e43cea08ab9abf803acba639b3)
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 ExprEngineInternalChecks.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "ClangSACheckers.h"
16 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
20 #include "llvm/ADT/ImmutableSet.h"
21 
22 using namespace clang;
23 using namespace ento;
24 
25 namespace {
26 class PthreadLockChecker
27   : public CheckerVisitor<PthreadLockChecker> {
28   BugType *BT;
29 public:
30   PthreadLockChecker() : BT(0) {}
31   static void *getTag() {
32     static int x = 0;
33     return &x;
34   }
35   void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE);
36 
37   void AcquireLock(CheckerContext &C, const CallExpr *CE,
38                    SVal lock, bool isTryLock);
39 
40   void ReleaseLock(CheckerContext &C, const CallExpr *CE,
41                     SVal lock);
42 
43 };
44 } // end anonymous namespace
45 
46 // GDM Entry for tracking lock state.
47 namespace { class LockSet {}; }
48 namespace clang {
49 namespace ento {
50 template <> struct GRStateTrait<LockSet> :
51   public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
52     static void* GDMIndex() { return PthreadLockChecker::getTag(); }
53 };
54 } // end GR namespace
55 } // end clang namespace
56 
57 static void RegisterPthreadLockChecker(ExprEngine &Eng) {
58   Eng.registerCheck(new PthreadLockChecker());
59 }
60 
61 void ento::registerPthreadLockChecker(CheckerManager &mgr) {
62   mgr.addCheckerRegisterFunction(RegisterPthreadLockChecker);
63 }
64 
65 
66 void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C,
67                                            const CallExpr *CE) {
68   const GRState *state = C.getState();
69   const Expr *Callee = CE->getCallee();
70   const FunctionTextRegion *R =
71     dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
72 
73   if (!R)
74     return;
75 
76   IdentifierInfo *II = R->getDecl()->getIdentifier();
77   if (!II)   // if no identifier, not a simple C function
78     return;
79   llvm::StringRef FName = II->getName();
80 
81   if (FName == "pthread_mutex_lock") {
82     if (CE->getNumArgs() != 1)
83       return;
84     AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false);
85   }
86   else if (FName == "pthread_mutex_trylock") {
87     if (CE->getNumArgs() != 1)
88       return;
89     AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true);
90   }
91   else if (FName == "pthread_mutex_unlock") {
92     if (CE->getNumArgs() != 1)
93       return;
94     ReleaseLock(C, CE, state->getSVal(CE->getArg(0)));
95   }
96 }
97 
98 void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
99                                      SVal lock, bool isTryLock) {
100 
101   const MemRegion *lockR = lock.getAsRegion();
102   if (!lockR)
103     return;
104 
105   const GRState *state = C.getState();
106 
107   SVal X = state->getSVal(CE);
108   if (X.isUnknownOrUndef())
109     return;
110 
111   DefinedSVal retVal = cast<DefinedSVal>(X);
112   const GRState *lockSucc = state;
113 
114   if (isTryLock) {
115       // Bifurcate the state, and allow a mode where the lock acquisition fails.
116     const GRState *lockFail;
117     llvm::tie(lockFail, lockSucc) = state->assume(retVal);
118     assert(lockFail && lockSucc);
119     C.addTransition(C.generateNode(CE, lockFail));
120   }
121   else {
122       // Assume that the return value was 0.
123     lockSucc = state->assume(retVal, false);
124     assert(lockSucc);
125   }
126 
127     // Record that the lock was acquired.
128   lockSucc = lockSucc->add<LockSet>(lockR);
129 
130   C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) :
131                   C.getPredecessor());
132 }
133 
134 void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
135                                      SVal lock) {
136 
137   const MemRegion *lockR = lock.getAsRegion();
138   if (!lockR)
139     return;
140 
141   const GRState *state = C.getState();
142 
143   // Record that the lock was released.
144   // FIXME: Handle unlocking locks that were never acquired.  This may
145   // require IPA for wrappers.
146   const GRState *unlockState = state->remove<LockSet>(lockR);
147 
148   if (state == unlockState)
149     return;
150 
151   C.addTransition(C.generateNode(CE, unlockState));
152 }
153