brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.8 KiB · 7d465ee Raw
363 lines · cpp
1//== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=//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 file defines InvalidPtrChecker which finds usages of possibly10// invalidated pointer.11// CERT SEI Rules ENV31-C and ENV34-C12// For more information see:13// https://wiki.sei.cmu.edu/confluence/x/8tYxBQ14// https://wiki.sei.cmu.edu/confluence/x/5NUxBQ15//===----------------------------------------------------------------------===//16 17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"19#include "clang/StaticAnalyzer/Core/Checker.h"20#include "clang/StaticAnalyzer/Core/CheckerManager.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"22#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"23#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"24 25using namespace clang;26using namespace ento;27 28namespace {29 30class InvalidPtrChecker31    : public Checker<check::Location, check::BeginFunction, check::PostCall> {32private:33  // For accurate emission of NoteTags, the BugType of this checker should have34  // a unique address.35  BugType InvalidPtrBugType{this, "Use of invalidated pointer",36                            categories::MemoryError};37 38  void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;39 40  using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,41                                                CheckerContext &C) const;42 43  // SEI CERT ENV31-C44 45  // If set to true, consider getenv calls as invalidating operations on the46  // environment variable buffer. This is implied in the standard, but in47  // practice does not cause problems (in the commonly used environments).48  bool InvalidatingGetEnv = false;49 50  // GetEnv can be treated invalidating and non-invalidating as well.51  const CallDescription GetEnvCall{CDM::CLibrary, {"getenv"}, 1};52 53  const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {54      {{CDM::CLibrary, {"setenv"}, 3},55       &InvalidPtrChecker::EnvpInvalidatingCall},56      {{CDM::CLibrary, {"unsetenv"}, 1},57       &InvalidPtrChecker::EnvpInvalidatingCall},58      {{CDM::CLibrary, {"putenv"}, 1},59       &InvalidPtrChecker::EnvpInvalidatingCall},60      {{CDM::CLibrary, {"_putenv_s"}, 2},61       &InvalidPtrChecker::EnvpInvalidatingCall},62      {{CDM::CLibrary, {"_wputenv_s"}, 2},63       &InvalidPtrChecker::EnvpInvalidatingCall},64  };65 66  void postPreviousReturnInvalidatingCall(const CallEvent &Call,67                                          CheckerContext &C) const;68 69  // SEI CERT ENV34-C70  const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {71      {{CDM::CLibrary, {"setlocale"}, 2},72       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},73      {{CDM::CLibrary, {"strerror"}, 1},74       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},75      {{CDM::CLibrary, {"localeconv"}, 0},76       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},77      {{CDM::CLibrary, {"asctime"}, 1},78       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},79  };80 81  // The private members of this checker corresponding to commandline options82  // are set in this function.83  friend void ento::registerInvalidPtrChecker(CheckerManager &);84 85public:86  // Obtain the environment pointer from 'main()' (if present).87  void checkBeginFunction(CheckerContext &C) const;88 89  // Handle functions in EnvpInvalidatingFunctions, that invalidate environment90  // pointer from 'main()'91  // Handle functions in PreviousCallInvalidatingFunctions.92  // Also, check if invalidated region is passed to a93  // conservatively evaluated function call as an argument.94  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;95 96  // Check if invalidated region is being dereferenced.97  void checkLocation(SVal l, bool isLoad, const Stmt *S,98                     CheckerContext &C) const;99 100private:101  const NoteTag *createEnvInvalidationNote(CheckerContext &C,102                                           ProgramStateRef State,103                                           StringRef FunctionName) const;104};105 106} // namespace107 108// Set of memory regions that were invalidated109REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)110 111// Stores the region of the environment pointer of 'main' (if present).112REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion, const MemRegion *)113 114// Stores the regions of environments returned by getenv calls.115REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions, const MemRegion *)116 117// Stores key-value pairs, where key is function declaration and value is118// pointer to memory region returned by previous call of this function119REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,120                               const MemRegion *)121 122const NoteTag *InvalidPtrChecker::createEnvInvalidationNote(123    CheckerContext &C, ProgramStateRef State, StringRef FunctionName) const {124 125  const MemRegion *MainRegion = State->get<MainEnvPtrRegion>();126  const auto GetenvRegions = State->get<GetenvEnvPtrRegions>();127 128  return C.getNoteTag([this, MainRegion, GetenvRegions,129                       FunctionName = std::string{FunctionName}](130                          PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {131    // Only handle the BugType of this checker.132    if (&BR.getBugType() != &InvalidPtrBugType)133      return;134 135    // Mark all regions that were interesting before as NOT interesting now136    // to avoid extra notes coming from invalidation points higher up the137    // bugpath. This ensures that only the last invalidation point is marked138    // with a note tag.139    llvm::SmallVector<std::string, 2> InvalidLocationNames;140    if (BR.isInteresting(MainRegion)) {141      BR.markNotInteresting(MainRegion);142      InvalidLocationNames.push_back("the environment parameter of 'main'");143    }144    bool InterestingGetenvFound = false;145    for (const MemRegion *MR : GetenvRegions) {146      if (BR.isInteresting(MR)) {147        BR.markNotInteresting(MR);148        if (!InterestingGetenvFound) {149          InterestingGetenvFound = true;150          InvalidLocationNames.push_back(151              "the environment returned by 'getenv'");152        }153      }154    }155 156    // Emit note tag message.157    if (InvalidLocationNames.size() >= 1)158      Out << '\'' << FunctionName << "' call may invalidate "159          << InvalidLocationNames[0];160    if (InvalidLocationNames.size() == 2)161      Out << ", and " << InvalidLocationNames[1];162  });163}164 165void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,166                                             CheckerContext &C) const {167  // This callevent invalidates all previously generated pointers to the168  // environment.169  ProgramStateRef State = C.getState();170  if (const MemRegion *MainEnvPtr = State->get<MainEnvPtrRegion>())171    State = State->add<InvalidMemoryRegions>(MainEnvPtr);172  for (const MemRegion *EnvPtr : State->get<GetenvEnvPtrRegions>())173    State = State->add<InvalidMemoryRegions>(EnvPtr);174 175  StringRef FunctionName = Call.getCalleeIdentifier()->getName();176  const NoteTag *InvalidationNote =177      createEnvInvalidationNote(C, State, FunctionName);178 179  C.addTransition(State, InvalidationNote);180}181 182void InvalidPtrChecker::postPreviousReturnInvalidatingCall(183    const CallEvent &Call, CheckerContext &C) const {184  ProgramStateRef State = C.getState();185 186  const NoteTag *Note = nullptr;187  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());188  // Invalidate the region of the previously returned pointer - if there was189  // one.190  if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {191    const MemRegion *PrevReg = *Reg;192    State = State->add<InvalidMemoryRegions>(PrevReg);193    Note = C.getNoteTag([this, PrevReg, FD](PathSensitiveBugReport &BR,194                                            llvm::raw_ostream &Out) {195      if (!BR.isInteresting(PrevReg) || &BR.getBugType() != &InvalidPtrBugType)196        return;197      Out << '\'';198      FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);199      Out << "' call may invalidate the result of the previous " << '\'';200      FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);201      Out << '\'';202    });203  }204 205  const LocationContext *LCtx = C.getLocationContext();206  const auto *CE = cast<CallExpr>(Call.getOriginExpr());207 208  // Function call will return a pointer to the new symbolic region.209  DefinedOrUnknownSVal RetVal =210      C.getSValBuilder().conjureSymbolVal(Call, C.blockCount());211  State = State->BindExpr(CE, LCtx, RetVal);212 213  const auto *SymRegOfRetVal =214      dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion());215  if (!SymRegOfRetVal)216    return;217 218  // Remember to this region.219  const MemRegion *MR = SymRegOfRetVal->getBaseRegion();220  State = State->set<PreviousCallResultMap>(FD, MR);221 222  ExplodedNode *Node = C.addTransition(State, Note);223  const NoteTag *PreviousCallNote = C.getNoteTag(224      [this, MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {225        if (!BR.isInteresting(MR) || &BR.getBugType() != &InvalidPtrBugType)226          return;227        Out << "previous function call was here";228      });229 230  C.addTransition(State, Node, PreviousCallNote);231}232 233// TODO: This seems really ugly. Simplify this.234static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,235                                                    const MemRegion *Reg) {236  while (Reg) {237    if (State->contains<InvalidMemoryRegions>(Reg))238      return Reg;239    const auto *SymBase = Reg->getSymbolicBase();240    if (!SymBase)241      break;242    const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());243    if (!SRV)244      break;245    Reg = SRV->getRegion();246    if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))247      Reg = VarReg;248  }249  return nullptr;250}251 252// Handle functions in EnvpInvalidatingFunctions, that invalidate environment253// pointer from 'main()' Also, check if invalidated region is passed to a254// function call as an argument.255void InvalidPtrChecker::checkPostCall(const CallEvent &Call,256                                      CheckerContext &C) const {257 258  ProgramStateRef State = C.getState();259 260  // Model 'getenv' calls261  if (GetEnvCall.matches(Call)) {262    const MemRegion *Region = Call.getReturnValue().getAsRegion();263    if (Region) {264      State = State->add<GetenvEnvPtrRegions>(Region);265      C.addTransition(State);266    }267  }268 269  // Check if function invalidates 'envp' argument of 'main'270  if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))271    (this->**Handler)(Call, C);272 273  // Check if function invalidates the result of previous call274  if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))275    (this->**Handler)(Call, C);276 277  // If pedantic mode is on, regard 'getenv' calls invalidating as well278  if (InvalidatingGetEnv && GetEnvCall.matches(Call))279    postPreviousReturnInvalidatingCall(Call, C);280 281  // Check if one of the arguments of the function call is invalidated282 283  // If call was inlined, don't report invalidated argument284  if (C.wasInlined)285    return;286 287  for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {288 289    if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(290            Call.getArgSVal(I).getAsRegion())) {291      if (const MemRegion *InvalidatedSymbolicBase =292              findInvalidatedSymbolicBase(State, SR)) {293        ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();294        if (!ErrorNode)295          return;296 297        SmallString<256> Msg;298        llvm::raw_svector_ostream Out(Msg);299        Out << "use of invalidated pointer '";300        Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,301                                        C.getASTContext().getPrintingPolicy());302        Out << "' in a function call";303 304        auto Report = std::make_unique<PathSensitiveBugReport>(305            InvalidPtrBugType, Out.str(), ErrorNode);306        Report->markInteresting(InvalidatedSymbolicBase);307        Report->addRange(Call.getArgSourceRange(I));308        C.emitReport(std::move(Report));309      }310    }311  }312}313 314// Obtain the environment pointer from 'main()', if present.315void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {316  if (!C.inTopFrame())317    return;318 319  const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());320  if (!FD || FD->param_size() != 3 || !FD->isMain())321    return;322 323  ProgramStateRef State = C.getState();324  const MemRegion *EnvpReg =325      State->getRegion(FD->parameters()[2], C.getLocationContext());326 327  // Save the memory region pointed by the environment pointer parameter of328  // 'main'.329  C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg));330}331 332// Check if invalidated region is being dereferenced.333void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,334                                      CheckerContext &C) const {335  ProgramStateRef State = C.getState();336 337  // Ignore memory operations involving 'non-invalidated' locations.338  const MemRegion *InvalidatedSymbolicBase =339      findInvalidatedSymbolicBase(State, Loc.getAsRegion());340  if (!InvalidatedSymbolicBase)341    return;342 343  ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();344  if (!ErrorNode)345    return;346 347  auto Report = std::make_unique<PathSensitiveBugReport>(348      InvalidPtrBugType, "dereferencing an invalid pointer", ErrorNode);349  Report->markInteresting(InvalidatedSymbolicBase);350  C.emitReport(std::move(Report));351}352 353void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {354  auto *Checker = Mgr.registerChecker<InvalidPtrChecker>();355  Checker->InvalidatingGetEnv =356      Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker,357                                                       "InvalidatingGetEnv");358}359 360bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {361  return true;362}363