Home | History | Annotate | Download | only in Checkers
      1 //== ReturnPointerRangeChecker.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 ReturnPointerRangeChecker, which is a path-sensitive check
     11 // which looks for an out-of-bound pointer being returned to callers.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "ClangSACheckers.h"
     16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     17 #include "clang/StaticAnalyzer/Core/Checker.h"
     18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     20 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
     21 
     22 using namespace clang;
     23 using namespace ento;
     24 
     25 namespace {
     26 class ReturnPointerRangeChecker :
     27     public Checker< check::PreStmt<ReturnStmt> > {
     28   mutable OwningPtr<BuiltinBug> BT;
     29 public:
     30     void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
     31 };
     32 }
     33 
     34 void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
     35                                              CheckerContext &C) const {
     36   ProgramStateRef state = C.getState();
     37 
     38   const Expr *RetE = RS->getRetValue();
     39   if (!RetE)
     40     return;
     41 
     42   SVal V = state->getSVal(RetE, C.getLocationContext());
     43   const MemRegion *R = V.getAsRegion();
     44 
     45   const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R);
     46   if (!ER)
     47     return;
     48 
     49   DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
     50   // Zero index is always in bound, this also passes ElementRegions created for
     51   // pointer casts.
     52   if (Idx.isZeroConstant())
     53     return;
     54   // FIXME: All of this out-of-bounds checking should eventually be refactored
     55   // into a common place.
     56 
     57   DefinedOrUnknownSVal NumElements
     58     = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
     59                                            ER->getValueType());
     60 
     61   ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
     62   ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
     63   if (StOutBound && !StInBound) {
     64     ExplodedNode *N = C.generateSink(StOutBound);
     65 
     66     if (!N)
     67       return;
     68 
     69     // FIXME: This bug correspond to CWE-466.  Eventually we should have bug
     70     // types explicitly reference such exploit categories (when applicable).
     71     if (!BT)
     72       BT.reset(new BuiltinBug("Return of pointer value outside of expected range",
     73            "Returned pointer value points outside the original object "
     74            "(potential buffer overflow)"));
     75 
     76     // FIXME: It would be nice to eventually make this diagnostic more clear,
     77     // e.g., by referencing the original declaration or by saying *why* this
     78     // reference is outside the range.
     79 
     80     // Generate a report for this bug.
     81     BugReport *report =
     82       new BugReport(*BT, BT->getDescription(), N);
     83 
     84     report->addRange(RetE->getSourceRange());
     85     C.emitReport(report);
     86   }
     87 }
     88 
     89 void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) {
     90   mgr.registerChecker<ReturnPointerRangeChecker>();
     91 }
     92