1218887Sdim//== ObjCAtSyncChecker.cpp - nil mutex checker for @synchronized -*- C++ -*--=//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim// This defines ObjCAtSyncChecker, a builtin check that checks for null pointers
11218887Sdim// used as mutexes for @synchronized.
12218887Sdim//
13218887Sdim//===----------------------------------------------------------------------===//
14218887Sdim
15218887Sdim#include "ClangSACheckers.h"
16234353Sdim#include "clang/AST/StmtObjC.h"
17249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
19218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
22218887Sdim
23218887Sdimusing namespace clang;
24218887Sdimusing namespace ento;
25218887Sdim
26218887Sdimnamespace {
27219077Sdimclass ObjCAtSyncChecker
28221345Sdim    : public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > {
29234353Sdim  mutable OwningPtr<BuiltinBug> BT_null;
30234353Sdim  mutable OwningPtr<BuiltinBug> BT_undef;
31219077Sdim
32218887Sdimpublic:
33219077Sdim  void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const;
34218887Sdim};
35218887Sdim} // end anonymous namespace
36218887Sdim
37219077Sdimvoid ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
38219077Sdim                                     CheckerContext &C) const {
39218887Sdim
40218887Sdim  const Expr *Ex = S->getSynchExpr();
41234353Sdim  ProgramStateRef state = C.getState();
42234353Sdim  SVal V = state->getSVal(Ex, C.getLocationContext());
43218887Sdim
44218887Sdim  // Uninitialized value used for the mutex?
45249423Sdim  if (V.getAs<UndefinedVal>()) {
46218887Sdim    if (ExplodedNode *N = C.generateSink()) {
47218887Sdim      if (!BT_undef)
48219077Sdim        BT_undef.reset(new BuiltinBug("Uninitialized value used as mutex "
49219077Sdim                                  "for @synchronized"));
50226633Sdim      BugReport *report =
51226633Sdim        new BugReport(*BT_undef, BT_undef->getDescription(), N);
52243830Sdim      bugreporter::trackNullOrUndefValue(N, Ex, *report);
53243830Sdim      C.emitReport(report);
54218887Sdim    }
55218887Sdim    return;
56218887Sdim  }
57218887Sdim
58218887Sdim  if (V.isUnknown())
59218887Sdim    return;
60218887Sdim
61218887Sdim  // Check for null mutexes.
62234353Sdim  ProgramStateRef notNullState, nullState;
63249423Sdim  llvm::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>());
64218887Sdim
65218887Sdim  if (nullState) {
66218887Sdim    if (!notNullState) {
67218887Sdim      // Generate an error node.  This isn't a sink since
68218887Sdim      // a null mutex just means no synchronization occurs.
69234353Sdim      if (ExplodedNode *N = C.addTransition(nullState)) {
70218887Sdim        if (!BT_null)
71219077Sdim          BT_null.reset(new BuiltinBug("Nil value used as mutex for @synchronized() "
72219077Sdim                                   "(no synchronization will occur)"));
73226633Sdim        BugReport *report =
74226633Sdim          new BugReport(*BT_null, BT_null->getDescription(), N);
75243830Sdim        bugreporter::trackNullOrUndefValue(N, Ex, *report);
76218887Sdim
77243830Sdim        C.emitReport(report);
78218887Sdim        return;
79218887Sdim      }
80218887Sdim    }
81218887Sdim    // Don't add a transition for 'nullState'.  If the value is
82218887Sdim    // under-constrained to be null or non-null, assume it is non-null
83218887Sdim    // afterwards.
84218887Sdim  }
85218887Sdim
86218887Sdim  if (notNullState)
87218887Sdim    C.addTransition(notNullState);
88218887Sdim}
89218887Sdim
90219077Sdimvoid ento::registerObjCAtSyncChecker(CheckerManager &mgr) {
91234353Sdim  if (mgr.getLangOpts().ObjC2)
92219077Sdim    mgr.registerChecker<ObjCAtSyncChecker>();
93219077Sdim}
94