1218887Sdim//== ReturnUndefChecker.cpp -------------------------------------*- 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 file defines ReturnUndefChecker, which is a path-sensitive
11218887Sdim// check which looks for undefined or garbage values being returned to the
12218887Sdim// caller.
13218887Sdim//
14218887Sdim//===----------------------------------------------------------------------===//
15218887Sdim
16221345Sdim#include "ClangSACheckers.h"
17249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
19221345Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20243830Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
21221345Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22218887Sdim
23218887Sdimusing namespace clang;
24218887Sdimusing namespace ento;
25218887Sdim
26218887Sdimnamespace {
27249423Sdimclass ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > {
28249423Sdim  mutable OwningPtr<BuiltinBug> BT_Undef;
29249423Sdim  mutable OwningPtr<BuiltinBug> BT_NullReference;
30249423Sdim
31249423Sdim  void emitUndef(CheckerContext &C, const Expr *RetE) const;
32249423Sdim  void checkReference(CheckerContext &C, const Expr *RetE,
33249423Sdim                      DefinedOrUnknownSVal RetVal) const;
34218887Sdimpublic:
35221345Sdim  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
36218887Sdim};
37218887Sdim}
38218887Sdim
39221345Sdimvoid ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
40221345Sdim                                      CheckerContext &C) const {
41218887Sdim  const Expr *RetE = RS->getRetValue();
42218887Sdim  if (!RetE)
43218887Sdim    return;
44249423Sdim  SVal RetVal = C.getSVal(RetE);
45249423Sdim
46243830Sdim  const StackFrameContext *SFC = C.getStackFrame();
47243830Sdim  QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());
48249423Sdim
49249423Sdim  if (RetVal.isUndef()) {
50249423Sdim    // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal
51249423Sdim    // to be returned in functions returning void to support this pattern:
52249423Sdim    //   void foo() {
53249423Sdim    //     return;
54249423Sdim    //   }
55249423Sdim    //   void test() {
56249423Sdim    //     return foo();
57249423Sdim    //   }
58251662Sdim    if (!RT.isNull() && RT->isVoidType())
59251662Sdim      return;
60251662Sdim
61251662Sdim    // Not all blocks have explicitly-specified return types; if the return type
62251662Sdim    // is not available, but the return value expression has 'void' type, assume
63251662Sdim    // Sema already checked it.
64251662Sdim    if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) &&
65251662Sdim        RetE->getType()->isVoidType())
66251662Sdim      return;
67251662Sdim
68251662Sdim    emitUndef(C, RetE);
69243830Sdim    return;
70249423Sdim  }
71243830Sdim
72249423Sdim  if (RT.isNull())
73249423Sdim    return;
74249423Sdim
75249423Sdim  if (RT->isReferenceType()) {
76249423Sdim    checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>());
77249423Sdim    return;
78249423Sdim  }
79249423Sdim}
80249423Sdim
81249423Sdimstatic void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
82249423Sdim                    const Expr *TrackingE = 0) {
83218887Sdim  ExplodedNode *N = C.generateSink();
84218887Sdim  if (!N)
85218887Sdim    return;
86218887Sdim
87249423Sdim  BugReport *Report = new BugReport(BT, BT.getDescription(), N);
88218887Sdim
89249423Sdim  Report->addRange(RetE->getSourceRange());
90249423Sdim  bugreporter::trackNullOrUndefValue(N, TrackingE ? TrackingE : RetE, *Report);
91249423Sdim
92249423Sdim  C.emitReport(Report);
93218887Sdim}
94221345Sdim
95249423Sdimvoid ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
96249423Sdim  if (!BT_Undef)
97249423Sdim    BT_Undef.reset(new BuiltinBug("Garbage return value",
98249423Sdim                                  "Undefined or garbage value "
99249423Sdim                                    "returned to caller"));
100249423Sdim  emitBug(C, *BT_Undef, RetE);
101249423Sdim}
102249423Sdim
103249423Sdimvoid ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
104249423Sdim                                        DefinedOrUnknownSVal RetVal) const {
105249423Sdim  ProgramStateRef StNonNull, StNull;
106249423Sdim  llvm::tie(StNonNull, StNull) = C.getState()->assume(RetVal);
107249423Sdim
108249423Sdim  if (StNonNull) {
109249423Sdim    // Going forward, assume the location is non-null.
110249423Sdim    C.addTransition(StNonNull);
111249423Sdim    return;
112249423Sdim  }
113249423Sdim
114249423Sdim  // The return value is known to be null. Emit a bug report.
115249423Sdim  if (!BT_NullReference)
116249423Sdim    BT_NullReference.reset(new BuiltinBug("Returning null reference"));
117249423Sdim
118249423Sdim  emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
119249423Sdim}
120249423Sdim
121221345Sdimvoid ento::registerReturnUndefChecker(CheckerManager &mgr) {
122221345Sdim  mgr.registerChecker<ReturnUndefChecker>();
123221345Sdim}
124