/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //== ObjCAtSyncChecker.cpp - nil mutex checker for @synchronized -*- C++ -*--=// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This defines ObjCAtSyncChecker, a builtin check that checks for null pointers |
10 | | // used as mutexes for @synchronized. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | | #include "clang/AST/StmtObjC.h" |
16 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
17 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
18 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
20 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
21 | | |
22 | | using namespace clang; |
23 | | using namespace ento; |
24 | | |
25 | | namespace { |
26 | | class ObjCAtSyncChecker |
27 | | : public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > { |
28 | | mutable std::unique_ptr<BugType> BT_null; |
29 | | mutable std::unique_ptr<BugType> BT_undef; |
30 | | |
31 | | public: |
32 | | void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const; |
33 | | }; |
34 | | } // end anonymous namespace |
35 | | |
36 | | void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, |
37 | 14 | CheckerContext &C) const { |
38 | | |
39 | 14 | const Expr *Ex = S->getSynchExpr(); |
40 | 14 | ProgramStateRef state = C.getState(); |
41 | 14 | SVal V = C.getSVal(Ex); |
42 | | |
43 | | // Uninitialized value used for the mutex? |
44 | 14 | if (isa<UndefinedVal>(V)) { |
45 | 2 | if (ExplodedNode *N = C.generateErrorNode()) { |
46 | 2 | if (!BT_undef) |
47 | 2 | BT_undef.reset(new BugType(this, "Uninitialized value used as mutex " |
48 | 2 | "for @synchronized")); |
49 | 2 | auto report = std::make_unique<PathSensitiveBugReport>( |
50 | 2 | *BT_undef, BT_undef->getDescription(), N); |
51 | 2 | bugreporter::trackExpressionValue(N, Ex, *report); |
52 | 2 | C.emitReport(std::move(report)); |
53 | 2 | } |
54 | 2 | return; |
55 | 2 | } |
56 | | |
57 | 12 | if (V.isUnknown()) |
58 | 0 | return; |
59 | | |
60 | | // Check for null mutexes. |
61 | 12 | ProgramStateRef notNullState, nullState; |
62 | 12 | std::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>()); |
63 | | |
64 | 12 | if (nullState) { |
65 | 10 | if (!notNullState) { |
66 | | // Generate an error node. This isn't a sink since |
67 | | // a null mutex just means no synchronization occurs. |
68 | 4 | if (ExplodedNode *N = C.generateNonFatalErrorNode(nullState)) { |
69 | 4 | if (!BT_null) |
70 | 2 | BT_null.reset( |
71 | 2 | new BugType(this, "Nil value used as mutex for @synchronized() " |
72 | 2 | "(no synchronization will occur)")); |
73 | 4 | auto report = std::make_unique<PathSensitiveBugReport>( |
74 | 4 | *BT_null, BT_null->getDescription(), N); |
75 | 4 | bugreporter::trackExpressionValue(N, Ex, *report); |
76 | | |
77 | 4 | C.emitReport(std::move(report)); |
78 | 4 | return; |
79 | 4 | } |
80 | 4 | } |
81 | | // Don't add a transition for 'nullState'. If the value is |
82 | | // under-constrained to be null or non-null, assume it is non-null |
83 | | // afterwards. |
84 | 10 | } |
85 | | |
86 | 8 | if (notNullState) |
87 | 8 | C.addTransition(notNullState); |
88 | 8 | } |
89 | | |
90 | 11 | void ento::registerObjCAtSyncChecker(CheckerManager &mgr) { |
91 | 11 | mgr.registerChecker<ObjCAtSyncChecker>(); |
92 | 11 | } |
93 | | |
94 | 98 | bool ento::shouldRegisterObjCAtSyncChecker(const CheckerManager &mgr) { |
95 | 98 | const LangOptions &LO = mgr.getLangOpts(); |
96 | 98 | return LO.ObjC; |
97 | 98 | } |