xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp (revision 507ff53e39e345e43cea08ab9abf803acba639b3)
1 //== ObjCAtSyncChecker.cpp - nil mutex checker for @synchronized -*- 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 ObjCAtSyncChecker, a builtin check that checks for null pointers
11 // used as mutexes for @synchronized.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "ClangSACheckers.h"
16 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18 #include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
21 
22 using namespace clang;
23 using namespace ento;
24 
25 namespace {
26 class ObjCAtSyncChecker : public CheckerVisitor<ObjCAtSyncChecker> {
27   BuiltinBug *BT_null;
28   BuiltinBug *BT_undef;
29 public:
30   ObjCAtSyncChecker() : BT_null(0), BT_undef(0) {}
31   static void *getTag() { static int tag = 0; return &tag; }
32   void PreVisitObjCAtSynchronizedStmt(CheckerContext &C,
33                                       const ObjCAtSynchronizedStmt *S);
34 };
35 } // end anonymous namespace
36 
37 static void RegisterObjCAtSyncChecker(ExprEngine &Eng) {
38   // @synchronized is an Objective-C 2 feature.
39   if (Eng.getContext().getLangOptions().ObjC2)
40     Eng.registerCheck(new ObjCAtSyncChecker());
41 }
42 
43 void ento::registerObjCAtSyncChecker(CheckerManager &mgr) {
44   mgr.addCheckerRegisterFunction(RegisterObjCAtSyncChecker);
45 }
46 
47 void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C,
48                                          const ObjCAtSynchronizedStmt *S) {
49 
50   const Expr *Ex = S->getSynchExpr();
51   const GRState *state = C.getState();
52   SVal V = state->getSVal(Ex);
53 
54   // Uninitialized value used for the mutex?
55   if (isa<UndefinedVal>(V)) {
56     if (ExplodedNode *N = C.generateSink()) {
57       if (!BT_undef)
58         BT_undef = new BuiltinBug("Uninitialized value used as mutex "
59                                   "for @synchronized");
60       EnhancedBugReport *report =
61         new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N);
62       report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex);
63       C.EmitReport(report);
64     }
65     return;
66   }
67 
68   if (V.isUnknown())
69     return;
70 
71   // Check for null mutexes.
72   const GRState *notNullState, *nullState;
73   llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V));
74 
75   if (nullState) {
76     if (!notNullState) {
77       // Generate an error node.  This isn't a sink since
78       // a null mutex just means no synchronization occurs.
79       if (ExplodedNode *N = C.generateNode(nullState)) {
80         if (!BT_null)
81           BT_null = new BuiltinBug("Nil value used as mutex for @synchronized() "
82                                    "(no synchronization will occur)");
83         EnhancedBugReport *report =
84           new EnhancedBugReport(*BT_null, BT_null->getDescription(), N);
85         report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
86                                   Ex);
87 
88         C.EmitReport(report);
89         return;
90       }
91     }
92     // Don't add a transition for 'nullState'.  If the value is
93     // under-constrained to be null or non-null, assume it is non-null
94     // afterwards.
95   }
96 
97   if (notNullState)
98     C.addTransition(notNullState);
99 }
100 
101