1218887Sdim//== ReturnPointerRangeChecker.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 ReturnPointerRangeChecker, which is a path-sensitive check
11218887Sdim// which looks for an out-of-bound pointer being returned to callers.
12218887Sdim//
13218887Sdim//===----------------------------------------------------------------------===//
14218887Sdim
15219077Sdim#include "ClangSACheckers.h"
16249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
18219077Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
19219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
21218887Sdim
22218887Sdimusing namespace clang;
23218887Sdimusing namespace ento;
24218887Sdim
25218887Sdimnamespace {
26218887Sdimclass ReturnPointerRangeChecker :
27221345Sdim    public Checker< check::PreStmt<ReturnStmt> > {
28234353Sdim  mutable OwningPtr<BuiltinBug> BT;
29218887Sdimpublic:
30219077Sdim    void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
31218887Sdim};
32218887Sdim}
33218887Sdim
34219077Sdimvoid ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
35219077Sdim                                             CheckerContext &C) const {
36234353Sdim  ProgramStateRef state = C.getState();
37218887Sdim
38218887Sdim  const Expr *RetE = RS->getRetValue();
39218887Sdim  if (!RetE)
40218887Sdim    return;
41218887Sdim
42234353Sdim  SVal V = state->getSVal(RetE, C.getLocationContext());
43218887Sdim  const MemRegion *R = V.getAsRegion();
44218887Sdim
45218887Sdim  const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R);
46218887Sdim  if (!ER)
47218887Sdim    return;
48218887Sdim
49249423Sdim  DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
50218887Sdim  // Zero index is always in bound, this also passes ElementRegions created for
51218887Sdim  // pointer casts.
52218887Sdim  if (Idx.isZeroConstant())
53218887Sdim    return;
54218887Sdim  // FIXME: All of this out-of-bounds checking should eventually be refactored
55218887Sdim  // into a common place.
56218887Sdim
57218887Sdim  DefinedOrUnknownSVal NumElements
58218887Sdim    = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
59218887Sdim                                           ER->getValueType());
60218887Sdim
61234353Sdim  ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
62234353Sdim  ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
63218887Sdim  if (StOutBound && !StInBound) {
64218887Sdim    ExplodedNode *N = C.generateSink(StOutBound);
65218887Sdim
66218887Sdim    if (!N)
67218887Sdim      return;
68218887Sdim
69218887Sdim    // FIXME: This bug correspond to CWE-466.  Eventually we should have bug
70218887Sdim    // types explicitly reference such exploit categories (when applicable).
71218887Sdim    if (!BT)
72219077Sdim      BT.reset(new BuiltinBug("Return of pointer value outside of expected range",
73218887Sdim           "Returned pointer value points outside the original object "
74219077Sdim           "(potential buffer overflow)"));
75218887Sdim
76218887Sdim    // FIXME: It would be nice to eventually make this diagnostic more clear,
77218887Sdim    // e.g., by referencing the original declaration or by saying *why* this
78218887Sdim    // reference is outside the range.
79218887Sdim
80218887Sdim    // Generate a report for this bug.
81226633Sdim    BugReport *report =
82226633Sdim      new BugReport(*BT, BT->getDescription(), N);
83218887Sdim
84218887Sdim    report->addRange(RetE->getSourceRange());
85243830Sdim    C.emitReport(report);
86218887Sdim  }
87218887Sdim}
88219077Sdim
89219077Sdimvoid ento::registerReturnPointerRangeChecker(CheckerManager &mgr) {
90219077Sdim  mgr.registerChecker<ReturnPointerRangeChecker>();
91219077Sdim}
92