Home | History | Annotate | Download | only in Checkers
      1 //== ReturnUndefChecker.cpp -------------------------------------*- 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 file defines ReturnUndefChecker, which is a path-sensitive
     11 // check which looks for undefined or garbage values being returned to the
     12 // caller.
     13 //
     14 //===----------------------------------------------------------------------===//
     15 
     16 #include "ClangSACheckers.h"
     17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     18 #include "clang/StaticAnalyzer/Core/Checker.h"
     19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     20 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
     21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     22 
     23 using namespace clang;
     24 using namespace ento;
     25 
     26 namespace {
     27 class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > {
     28   mutable std::unique_ptr<BuiltinBug> BT_Undef;
     29   mutable std::unique_ptr<BuiltinBug> BT_NullReference;
     30 
     31   void emitUndef(CheckerContext &C, const Expr *RetE) const;
     32   void checkReference(CheckerContext &C, const Expr *RetE,
     33                       DefinedOrUnknownSVal RetVal) const;
     34 public:
     35   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
     36 };
     37 }
     38 
     39 void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
     40                                       CheckerContext &C) const {
     41   const Expr *RetE = RS->getRetValue();
     42   if (!RetE)
     43     return;
     44   SVal RetVal = C.getSVal(RetE);
     45 
     46   const StackFrameContext *SFC = C.getStackFrame();
     47   QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());
     48 
     49   if (RetVal.isUndef()) {
     50     // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal
     51     // to be returned in functions returning void to support this pattern:
     52     //   void foo() {
     53     //     return;
     54     //   }
     55     //   void test() {
     56     //     return foo();
     57     //   }
     58     if (!RT.isNull() && RT->isVoidType())
     59       return;
     60 
     61     // Not all blocks have explicitly-specified return types; if the return type
     62     // is not available, but the return value expression has 'void' type, assume
     63     // Sema already checked it.
     64     if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) &&
     65         RetE->getType()->isVoidType())
     66       return;
     67 
     68     emitUndef(C, RetE);
     69     return;
     70   }
     71 
     72   if (RT.isNull())
     73     return;
     74 
     75   if (RT->isReferenceType()) {
     76     checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>());
     77     return;
     78   }
     79 }
     80 
     81 static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
     82                     const Expr *TrackingE = nullptr) {
     83   ExplodedNode *N = C.generateSink();
     84   if (!N)
     85     return;
     86 
     87   BugReport *Report = new BugReport(BT, BT.getDescription(), N);
     88 
     89   Report->addRange(RetE->getSourceRange());
     90   bugreporter::trackNullOrUndefValue(N, TrackingE ? TrackingE : RetE, *Report);
     91 
     92   C.emitReport(Report);
     93 }
     94 
     95 void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
     96   if (!BT_Undef)
     97     BT_Undef.reset(
     98         new BuiltinBug(this, "Garbage return value",
     99                        "Undefined or garbage value returned to caller"));
    100   emitBug(C, *BT_Undef, RetE);
    101 }
    102 
    103 void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
    104                                         DefinedOrUnknownSVal RetVal) const {
    105   ProgramStateRef StNonNull, StNull;
    106   std::tie(StNonNull, StNull) = C.getState()->assume(RetVal);
    107 
    108   if (StNonNull) {
    109     // Going forward, assume the location is non-null.
    110     C.addTransition(StNonNull);
    111     return;
    112   }
    113 
    114   // The return value is known to be null. Emit a bug report.
    115   if (!BT_NullReference)
    116     BT_NullReference.reset(new BuiltinBug(this, "Returning null reference"));
    117 
    118   emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
    119 }
    120 
    121 void ento::registerReturnUndefChecker(CheckerManager &mgr) {
    122   mgr.registerChecker<ReturnUndefChecker>();
    123 }
    124