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