brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.7 KiB · 37f5ec3 Raw
511 lines · cpp
1//===-- DereferenceChecker.cpp - Null dereference checker -----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This defines NullDerefChecker, a builtin check in ExprEngine that performs10// checks for null pointers at loads and stores.11//12//===----------------------------------------------------------------------===//13 14#include "clang/AST/ExprObjC.h"15#include "clang/Basic/TargetInfo.h"16#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CheckerContext.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"22#include "llvm/Support/FormatVariadic.h"23#include "llvm/Support/raw_ostream.h"24 25using namespace clang;26using namespace ento;27 28namespace {29 30class DerefBugType : public BugType {31  StringRef ArrayMsg, FieldMsg;32 33public:34  DerefBugType(CheckerFrontend *FE, StringRef Desc, const char *AMsg,35               const char *FMsg = nullptr)36      : BugType(FE, Desc), ArrayMsg(AMsg), FieldMsg(FMsg ? FMsg : AMsg) {}37  StringRef getArrayMsg() const { return ArrayMsg; }38  StringRef getFieldMsg() const { return FieldMsg; }39};40 41class DereferenceChecker42    : public CheckerFamily<check::Location, check::Bind,43                           check::PreStmt<BinaryOperator>,44                           EventDispatcher<ImplicitNullDerefEvent>> {45  void reportDerefBug(const DerefBugType &BT, ProgramStateRef State,46                      const Stmt *S, CheckerContext &C) const;47 48  bool suppressReport(CheckerContext &C, const Expr *E) const;49 50public:51  void checkLocation(SVal location, bool isLoad, const Stmt* S,52                     CheckerContext &C) const;53  void checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,54                 CheckerContext &C) const;55  void checkPreStmt(const BinaryOperator *Op, CheckerContext &C) const;56 57  static void AddDerefSource(raw_ostream &os,58                             SmallVectorImpl<SourceRange> &Ranges,59                             const Expr *Ex, const ProgramState *state,60                             const LocationContext *LCtx,61                             bool loadedFrom = false);62 63  CheckerFrontend NullDerefChecker, FixedDerefChecker, NullPointerArithmChecker;64  const DerefBugType NullBug{&NullDerefChecker, "Dereference of null pointer",65                             "a null pointer dereference",66                             "a dereference of a null pointer"};67  const DerefBugType UndefBug{&NullDerefChecker,68                              "Dereference of undefined pointer value",69                              "an undefined pointer dereference",70                              "a dereference of an undefined pointer value"};71  const DerefBugType LabelBug{&NullDerefChecker,72                              "Dereference of the address of a label",73                              "an undefined pointer dereference",74                              "a dereference of an address of a label"};75  const DerefBugType FixedAddressBug{&FixedDerefChecker,76                                     "Dereference of a fixed address",77                                     "a dereference of a fixed address"};78  const BugType NullPointerArithmBug{79      &NullPointerArithmChecker,80      "Possibly undefined arithmetic operation involving a null pointer"};81 82  StringRef getDebugTag() const override { return "DereferenceChecker"; }83};84 85struct ValueDescStr {86  SmallVectorImpl<SourceRange> &Ranges;87  const Expr *Ex;88  const ProgramState *State;89  const LocationContext *LCtx;90  bool IsPointer;91  ConditionTruthVal IsNull;92};93 94} // end anonymous namespace95 96void97DereferenceChecker::AddDerefSource(raw_ostream &os,98                                   SmallVectorImpl<SourceRange> &Ranges,99                                   const Expr *Ex,100                                   const ProgramState *state,101                                   const LocationContext *LCtx,102                                   bool loadedFrom) {103  Ex = Ex->IgnoreParenLValueCasts();104  switch (Ex->getStmtClass()) {105    default:106      break;107    case Stmt::DeclRefExprClass: {108      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);109      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {110        os << " (" << (loadedFrom ? "loaded from" : "from")111           << " variable '" <<  VD->getName() << "')";112        Ranges.push_back(DR->getSourceRange());113      }114      break;115    }116    case Stmt::MemberExprClass: {117      const MemberExpr *ME = cast<MemberExpr>(Ex);118      os << " (" << (loadedFrom ? "loaded from" : "via")119         << " field '" << ME->getMemberNameInfo() << "')";120      SourceLocation L = ME->getMemberLoc();121      Ranges.push_back(SourceRange(L, L));122      break;123    }124    case Stmt::ObjCIvarRefExprClass: {125      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);126      os << " (" << (loadedFrom ? "loaded from" : "via")127         << " ivar '" << IV->getDecl()->getName() << "')";128      SourceLocation L = IV->getLocation();129      Ranges.push_back(SourceRange(L, L));130      break;131    }132  }133}134 135static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){136  const Expr *E = nullptr;137 138  // Walk through lvalue casts to get the original expression139  // that syntactically caused the load.140  if (const Expr *expr = dyn_cast<Expr>(S))141    E = expr->IgnoreParenLValueCasts();142 143  if (IsBind) {144    const VarDecl *VD;145    const Expr *Init;146    std::tie(VD, Init) = parseAssignment(S);147    if (VD && Init)148      E = Init;149  }150  return E;151}152 153bool DereferenceChecker::suppressReport(CheckerContext &C,154                                        const Expr *E) const {155  // Do not report dereferences on memory that use address space #256, #257,156  // and #258. Those address spaces are used when dereferencing address spaces157  // relative to the GS, FS, and SS segments on x86/x86-64 targets.158  // Dereferencing a null pointer in these address spaces is not defined159  // as an error. All other null dereferences in other address spaces160  // are defined as an error unless explicitly defined.161  // See https://clang.llvm.org/docs/LanguageExtensions.html, the section162  // "X86/X86-64 Language Extensions"163 164  QualType Ty = E->getType();165  if (!Ty.hasAddressSpace())166    return false;167  if (C.getAnalysisManager()168          .getAnalyzerOptions()169          .ShouldSuppressAddressSpaceDereferences)170    return true;171 172  const llvm::Triple::ArchType Arch =173      C.getASTContext().getTargetInfo().getTriple().getArch();174 175  if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {176    switch (toTargetAddressSpace(E->getType().getAddressSpace())) {177    case 256:178    case 257:179    case 258:180      return true;181    }182  }183  return false;184}185 186static bool isDeclRefExprToReference(const Expr *E) {187  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))188    return DRE->getDecl()->getType()->isReferenceType();189  return false;190}191 192void DereferenceChecker::reportDerefBug(const DerefBugType &BT,193                                        ProgramStateRef State, const Stmt *S,194                                        CheckerContext &C) const {195  if (&BT == &FixedAddressBug) {196    if (!FixedDerefChecker.isEnabled())197      // Deliberately don't add a sink node if check is disabled.198      // This situation may be valid in special cases.199      return;200  } else {201    if (!NullDerefChecker.isEnabled()) {202      C.addSink();203      return;204    }205  }206 207  // Generate an error node.208  ExplodedNode *N = C.generateErrorNode(State);209  if (!N)210    return;211 212  SmallString<100> Buf;213  llvm::raw_svector_ostream Out(Buf);214 215  SmallVector<SourceRange, 2> Ranges;216 217  switch (S->getStmtClass()) {218  case Stmt::ArraySubscriptExprClass: {219    Out << "Array access";220    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);221    AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),222                   N->getLocationContext());223    Out << " results in " << BT.getArrayMsg();224    break;225  }226  case Stmt::ArraySectionExprClass: {227    Out << "Array access";228    const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);229    AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),230                   N->getLocationContext());231    Out << " results in " << BT.getArrayMsg();232    break;233  }234  case Stmt::UnaryOperatorClass: {235    Out << BT.getDescription();236    const UnaryOperator *U = cast<UnaryOperator>(S);237    AddDerefSource(Out, Ranges, U->getSubExpr()->IgnoreParens(), State.get(),238                   N->getLocationContext(), true);239    break;240  }241  case Stmt::MemberExprClass: {242    const MemberExpr *M = cast<MemberExpr>(S);243    if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {244      Out << "Access to field '" << M->getMemberNameInfo() << "' results in "245          << BT.getFieldMsg();246      AddDerefSource(Out, Ranges, M->getBase()->IgnoreParenCasts(), State.get(),247                     N->getLocationContext(), true);248    }249    break;250  }251  case Stmt::ObjCIvarRefExprClass: {252    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);253    Out << "Access to instance variable '" << *IV->getDecl() << "' results in "254        << BT.getFieldMsg();255    AddDerefSource(Out, Ranges, IV->getBase()->IgnoreParenCasts(), State.get(),256                   N->getLocationContext(), true);257    break;258  }259  default:260    break;261  }262 263  auto BR = std::make_unique<PathSensitiveBugReport>(264      BT, Buf.empty() ? BT.getDescription() : Buf.str(), N);265 266  bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *BR);267 268  for (const auto &R : Ranges)269    BR->addRange(R);270 271  C.emitReport(std::move(BR));272}273 274void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,275                                       CheckerContext &C) const {276  // Check for dereference of an undefined value.277  if (l.isUndef()) {278    const Expr *DerefExpr = getDereferenceExpr(S);279    if (!suppressReport(C, DerefExpr))280      reportDerefBug(UndefBug, C.getState(), DerefExpr, C);281    return;282  }283 284  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();285 286  // Check for null dereferences.287  if (!isa<Loc>(location))288    return;289 290  ProgramStateRef state = C.getState();291 292  ProgramStateRef notNullState, nullState;293  std::tie(notNullState, nullState) = state->assume(location);294 295  if (nullState) {296    if (!notNullState) {297      // We know that 'location' can only be null.  This is what298      // we call an "explicit" null dereference.299      const Expr *expr = getDereferenceExpr(S);300      if (!suppressReport(C, expr)) {301        reportDerefBug(NullBug, nullState, expr, C);302        return;303      }304    }305 306    // Otherwise, we have the case where the location could either be307    // null or not-null.  Record the error node as an "implicit" null308    // dereference.309    if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {310      ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),311                                      /*IsDirectDereference=*/true};312      dispatchEvent(event);313    }314  }315 316  if (location.isConstant()) {317    const Expr *DerefExpr = getDereferenceExpr(S, isLoad);318    if (!suppressReport(C, DerefExpr))319      reportDerefBug(FixedAddressBug, notNullState, DerefExpr, C);320    return;321  }322 323  // From this point forward, we know that the location is not null.324  C.addTransition(notNullState);325}326 327void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,328                                   bool AtDeclInit, CheckerContext &C) const {329  // If we're binding to a reference, check if the value is known to be null.330  if (V.isUndef())331    return;332 333  // One should never write to label addresses.334  if (auto Label = L.getAs<loc::GotoLabel>()) {335    reportDerefBug(LabelBug, C.getState(), S, C);336    return;337  }338 339  const MemRegion *MR = L.getAsRegion();340  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);341  if (!TVR)342    return;343 344  if (!TVR->getValueType()->isReferenceType())345    return;346 347  ProgramStateRef State = C.getState();348 349  ProgramStateRef StNonNull, StNull;350  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());351 352  if (StNull) {353    if (!StNonNull) {354      const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);355      if (!suppressReport(C, expr)) {356        reportDerefBug(NullBug, StNull, expr, C);357        return;358      }359    }360 361    // At this point the value could be either null or non-null.362    // Record this as an "implicit" null dereference.363    if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {364      ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,365                                      &C.getBugReporter(),366                                      /*IsDirectDereference=*/true};367      dispatchEvent(event);368    }369  }370 371  if (V.isConstant()) {372    const Expr *DerefExpr = getDereferenceExpr(S, true);373    if (!suppressReport(C, DerefExpr))374      reportDerefBug(FixedAddressBug, State, DerefExpr, C);375    return;376  }377 378  // Unlike a regular null dereference, initializing a reference with a379  // dereferenced null pointer does not actually cause a runtime exception in380  // Clang's implementation of references.381  //382  //   int &r = *p; // safe??383  //   if (p != NULL) return; // uh-oh384  //   r = 5; // trap here385  //386  // The standard says this is invalid as soon as we try to create a "null387  // reference" (there is no such thing), but turning this into an assumption388  // that 'p' is never null will not match our actual runtime behavior.389  // So we do not record this assumption, allowing us to warn on the last line390  // of this example.391  //392  // We do need to add a transition because we may have generated a sink for393  // the "implicit" null dereference.394  C.addTransition(State, this);395}396 397namespace llvm {398template <> struct format_provider<ValueDescStr> {399  static void format(const ValueDescStr &V, raw_ostream &Stream,400                     StringRef Style) {401    static const char *ValueStr[2][3] = {402        {"zero", "nonzero integer value", "probably nonzero integer value"},403        {"null pointer", "non-null pointer", "probably non-null pointer"},404    };405    Stream406        << ValueStr[V.IsPointer][V.IsNull.isConstrainedTrue()407                                     ? 0408                                     : (V.IsNull.isConstrainedFalse() ? 1 : 2)];409    DereferenceChecker::AddDerefSource(Stream, V.Ranges, V.Ex, V.State, V.LCtx,410                                       false);411  }412};413} // namespace llvm414 415void DereferenceChecker::checkPreStmt(const BinaryOperator *Op,416                                      CheckerContext &C) const {417  if (!Op->isAdditiveOp() || !NullPointerArithmChecker.isEnabled())418    return;419  const Expr *E1 = Op->getLHS();420  const Expr *E2 = Op->getRHS();421  QualType T1 = E1->getType().getCanonicalType();422  QualType T2 = E2->getType().getCanonicalType();423  bool T1IsPointer = T1->isPointerType();424  bool T2IsPointer = T2->isPointerType();425  if (T1->isIntegerType() && T2->isIntegerType())426    return;427  if (!T1IsPointer && !T1->isIntegerType() && !T2IsPointer &&428      !T2->isIntegerType())429    return;430 431  ProgramStateRef State = C.getState();432  ConditionTruthVal V1IsNull = State->isNull(C.getSVal(E1));433  ConditionTruthVal V2IsNull = State->isNull(C.getSVal(E2));434  bool IsConstrained = true;435 436  // Check cases 'NULL + x' and 'NULL - x'437  if (T1IsPointer && !T2IsPointer) {438    if (!V1IsNull.isConstrainedTrue() || V2IsNull.isConstrainedTrue())439      return;440    IsConstrained = V2IsNull.isConstrainedFalse();441  }442 443  // Check case 'x + NULL'444  if (!T1IsPointer && T2IsPointer) {445    if (V1IsNull.isConstrainedTrue() || !V2IsNull.isConstrainedTrue())446      return;447    IsConstrained = V1IsNull.isConstrainedFalse();448  }449 450  // Check case 'NULL - p' or 'p - NULL'451  if (T1IsPointer && T2IsPointer) {452    if (!V1IsNull.isConstrainedTrue() && !V2IsNull.isConstrainedTrue())453      return;454    if (V1IsNull.isConstrainedTrue() && V2IsNull.isConstrainedTrue())455      return;456    IsConstrained =457        V1IsNull.isConstrainedFalse() || V2IsNull.isConstrainedFalse();458  }459 460  SmallVector<SourceRange, 2> Ranges;461  const char *OpcodeStr =462      Op->getOpcode() == BO_Add ? "Addition" : "Subtraction";463  const char *ResultStr = IsConstrained ? "results" : "may result";464  ValueDescStr DerefArg1{465      Ranges, E1, State.get(), C.getLocationContext(), T1IsPointer, V1IsNull};466  ValueDescStr DerefArg2{467      Ranges, E2, State.get(), C.getLocationContext(), T2IsPointer, V2IsNull};468  std::string Msg =469      llvm::formatv("{0} of a {1} and a {2} {3} in undefined behavior",470                    OpcodeStr, DerefArg1, DerefArg2, ResultStr);471 472  ExplodedNode *N = C.generateErrorNode(State);473  if (!N)474    return;475  auto BR =476      std::make_unique<PathSensitiveBugReport>(NullPointerArithmBug, Msg, N);477  if (V1IsNull.isConstrainedTrue())478    bugreporter::trackExpressionValue(N, E1, *BR);479  if (V2IsNull.isConstrainedTrue())480    bugreporter::trackExpressionValue(N, E2, *BR);481  for (const auto &R : Ranges)482    BR->addRange(R);483 484  C.emitReport(std::move(BR));485}486 487void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {488  Mgr.getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);489}490 491bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {492  return true;493}494 495void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {496  Mgr.getChecker<DereferenceChecker>()->FixedDerefChecker.enable(Mgr);497}498 499bool ento::shouldRegisterFixedAddressDereferenceChecker(500    const CheckerManager &) {501  return true;502}503 504void ento::registerNullPointerArithmChecker(CheckerManager &Mgr) {505  Mgr.getChecker<DereferenceChecker>()->NullPointerArithmChecker.enable(Mgr);506}507 508bool ento::shouldRegisterNullPointerArithmChecker(const CheckerManager &) {509  return true;510}511