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