clang API Documentation

ReturnUndefChecker.cpp
Go to the documentation of this file.
00001 //== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file defines ReturnUndefChecker, which is a path-sensitive
00011 // check which looks for undefined or garbage values being returned to the
00012 // caller.
00013 //
00014 //===----------------------------------------------------------------------===//
00015 
00016 #include "ClangSACheckers.h"
00017 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
00018 #include "clang/StaticAnalyzer/Core/Checker.h"
00019 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
00020 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
00021 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
00022 
00023 using namespace clang;
00024 using namespace ento;
00025 
00026 namespace {
00027 class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > {
00028   mutable std::unique_ptr<BuiltinBug> BT_Undef;
00029   mutable std::unique_ptr<BuiltinBug> BT_NullReference;
00030 
00031   void emitUndef(CheckerContext &C, const Expr *RetE) const;
00032   void checkReference(CheckerContext &C, const Expr *RetE,
00033                       DefinedOrUnknownSVal RetVal) const;
00034 public:
00035   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
00036 };
00037 }
00038 
00039 void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
00040                                       CheckerContext &C) const {
00041   const Expr *RetE = RS->getRetValue();
00042   if (!RetE)
00043     return;
00044   SVal RetVal = C.getSVal(RetE);
00045 
00046   const StackFrameContext *SFC = C.getStackFrame();
00047   QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());
00048 
00049   if (RetVal.isUndef()) {
00050     // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal
00051     // to be returned in functions returning void to support this pattern:
00052     //   void foo() {
00053     //     return;
00054     //   }
00055     //   void test() {
00056     //     return foo();
00057     //   }
00058     if (!RT.isNull() && RT->isVoidType())
00059       return;
00060 
00061     // Not all blocks have explicitly-specified return types; if the return type
00062     // is not available, but the return value expression has 'void' type, assume
00063     // Sema already checked it.
00064     if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) &&
00065         RetE->getType()->isVoidType())
00066       return;
00067 
00068     emitUndef(C, RetE);
00069     return;
00070   }
00071 
00072   if (RT.isNull())
00073     return;
00074 
00075   if (RT->isReferenceType()) {
00076     checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>());
00077     return;
00078   }
00079 }
00080 
00081 static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
00082                     const Expr *TrackingE = nullptr) {
00083   ExplodedNode *N = C.generateSink();
00084   if (!N)
00085     return;
00086 
00087   BugReport *Report = new BugReport(BT, BT.getDescription(), N);
00088 
00089   Report->addRange(RetE->getSourceRange());
00090   bugreporter::trackNullOrUndefValue(N, TrackingE ? TrackingE : RetE, *Report);
00091 
00092   C.emitReport(Report);
00093 }
00094 
00095 void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
00096   if (!BT_Undef)
00097     BT_Undef.reset(
00098         new BuiltinBug(this, "Garbage return value",
00099                        "Undefined or garbage value returned to caller"));
00100   emitBug(C, *BT_Undef, RetE);
00101 }
00102 
00103 void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
00104                                         DefinedOrUnknownSVal RetVal) const {
00105   ProgramStateRef StNonNull, StNull;
00106   std::tie(StNonNull, StNull) = C.getState()->assume(RetVal);
00107 
00108   if (StNonNull) {
00109     // Going forward, assume the location is non-null.
00110     C.addTransition(StNonNull);
00111     return;
00112   }
00113 
00114   // The return value is known to be null. Emit a bug report.
00115   if (!BT_NullReference)
00116     BT_NullReference.reset(new BuiltinBug(this, "Returning null reference"));
00117 
00118   emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
00119 }
00120 
00121 void ento::registerReturnUndefChecker(CheckerManager &mgr) {
00122   mgr.registerChecker<ReturnUndefChecker>();
00123 }