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