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