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