1349cc55cSDimitry Andric //== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=// 2349cc55cSDimitry Andric // 3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6349cc55cSDimitry Andric // 7349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 8349cc55cSDimitry Andric // 9349cc55cSDimitry Andric // This file defines InvalidPtrChecker which finds usages of possibly 10349cc55cSDimitry Andric // invalidated pointer. 11349cc55cSDimitry Andric // CERT SEI Rules ENV31-C and ENV34-C 12349cc55cSDimitry Andric // For more information see: 13349cc55cSDimitry Andric // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ 14349cc55cSDimitry Andric // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ 15349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 16349cc55cSDimitry Andric 17349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 20349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 22349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 23349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 24349cc55cSDimitry Andric 25349cc55cSDimitry Andric using namespace clang; 26349cc55cSDimitry Andric using namespace ento; 27349cc55cSDimitry Andric 28349cc55cSDimitry Andric namespace { 29349cc55cSDimitry Andric 30349cc55cSDimitry Andric class InvalidPtrChecker 31349cc55cSDimitry Andric : public Checker<check::Location, check::BeginFunction, check::PostCall> { 32349cc55cSDimitry Andric private: 33349cc55cSDimitry Andric BugType BT{this, "Use of invalidated pointer", categories::MemoryError}; 34349cc55cSDimitry Andric 35349cc55cSDimitry Andric void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const; 36349cc55cSDimitry Andric 37349cc55cSDimitry Andric using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call, 38349cc55cSDimitry Andric CheckerContext &C) const; 39349cc55cSDimitry Andric 40349cc55cSDimitry Andric // SEI CERT ENV31-C 41349cc55cSDimitry Andric const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = { 42*bdd1243dSDimitry Andric {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, 43*bdd1243dSDimitry Andric {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, 44*bdd1243dSDimitry Andric {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, 45*bdd1243dSDimitry Andric {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, 46*bdd1243dSDimitry Andric {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, 47349cc55cSDimitry Andric }; 48349cc55cSDimitry Andric 49349cc55cSDimitry Andric void postPreviousReturnInvalidatingCall(const CallEvent &Call, 50349cc55cSDimitry Andric CheckerContext &C) const; 51349cc55cSDimitry Andric 52349cc55cSDimitry Andric // SEI CERT ENV34-C 53349cc55cSDimitry Andric const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = { 54*bdd1243dSDimitry Andric {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 55*bdd1243dSDimitry Andric {{{"setlocale"}, 2}, 56349cc55cSDimitry Andric &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 57*bdd1243dSDimitry Andric {{{"strerror"}, 1}, 58349cc55cSDimitry Andric &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 59*bdd1243dSDimitry Andric {{{"localeconv"}, 0}, 60*bdd1243dSDimitry Andric &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 61*bdd1243dSDimitry Andric {{{"asctime"}, 1}, 62*bdd1243dSDimitry Andric &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 63349cc55cSDimitry Andric }; 64349cc55cSDimitry Andric 65349cc55cSDimitry Andric public: 66349cc55cSDimitry Andric // Obtain the environment pointer from 'main()' (if present). 67349cc55cSDimitry Andric void checkBeginFunction(CheckerContext &C) const; 68349cc55cSDimitry Andric 69349cc55cSDimitry Andric // Handle functions in EnvpInvalidatingFunctions, that invalidate environment 70349cc55cSDimitry Andric // pointer from 'main()' 71349cc55cSDimitry Andric // Handle functions in PreviousCallInvalidatingFunctions. 72349cc55cSDimitry Andric // Also, check if invalidated region is passed to a 73349cc55cSDimitry Andric // conservatively evaluated function call as an argument. 74349cc55cSDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 75349cc55cSDimitry Andric 76349cc55cSDimitry Andric // Check if invalidated region is being dereferenced. 77349cc55cSDimitry Andric void checkLocation(SVal l, bool isLoad, const Stmt *S, 78349cc55cSDimitry Andric CheckerContext &C) const; 79349cc55cSDimitry Andric }; 80349cc55cSDimitry Andric 81349cc55cSDimitry Andric } // namespace 82349cc55cSDimitry Andric 83349cc55cSDimitry Andric // Set of memory regions that were invalidated 84349cc55cSDimitry Andric REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *) 85349cc55cSDimitry Andric 86349cc55cSDimitry Andric // Stores the region of the environment pointer of 'main' (if present). 8781ad6265SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *) 88349cc55cSDimitry Andric 89349cc55cSDimitry Andric // Stores key-value pairs, where key is function declaration and value is 90349cc55cSDimitry Andric // pointer to memory region returned by previous call of this function 91349cc55cSDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, 92349cc55cSDimitry Andric const MemRegion *) 93349cc55cSDimitry Andric 94349cc55cSDimitry Andric void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call, 95349cc55cSDimitry Andric CheckerContext &C) const { 96349cc55cSDimitry Andric StringRef FunctionName = Call.getCalleeIdentifier()->getName(); 97349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 9881ad6265SDimitry Andric const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>(); 9981ad6265SDimitry Andric if (!SymbolicEnvPtrRegion) 100349cc55cSDimitry Andric return; 101349cc55cSDimitry Andric 102349cc55cSDimitry Andric State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion); 103349cc55cSDimitry Andric 104349cc55cSDimitry Andric const NoteTag *Note = 105349cc55cSDimitry Andric C.getNoteTag([SymbolicEnvPtrRegion, FunctionName]( 106349cc55cSDimitry Andric PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { 107349cc55cSDimitry Andric if (!BR.isInteresting(SymbolicEnvPtrRegion)) 108349cc55cSDimitry Andric return; 109349cc55cSDimitry Andric Out << '\'' << FunctionName 110349cc55cSDimitry Andric << "' call may invalidate the environment parameter of 'main'"; 111349cc55cSDimitry Andric }); 112349cc55cSDimitry Andric 113349cc55cSDimitry Andric C.addTransition(State, Note); 114349cc55cSDimitry Andric } 115349cc55cSDimitry Andric 116349cc55cSDimitry Andric void InvalidPtrChecker::postPreviousReturnInvalidatingCall( 117349cc55cSDimitry Andric const CallEvent &Call, CheckerContext &C) const { 118349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 119349cc55cSDimitry Andric 120349cc55cSDimitry Andric const NoteTag *Note = nullptr; 121349cc55cSDimitry Andric const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 122349cc55cSDimitry Andric // Invalidate the region of the previously returned pointer - if there was 123349cc55cSDimitry Andric // one. 124349cc55cSDimitry Andric if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) { 125349cc55cSDimitry Andric const MemRegion *PrevReg = *Reg; 126349cc55cSDimitry Andric State = State->add<InvalidMemoryRegions>(PrevReg); 127349cc55cSDimitry Andric Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR, 128349cc55cSDimitry Andric llvm::raw_ostream &Out) { 129349cc55cSDimitry Andric if (!BR.isInteresting(PrevReg)) 130349cc55cSDimitry Andric return; 131349cc55cSDimitry Andric Out << '\''; 132349cc55cSDimitry Andric FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); 13381ad6265SDimitry Andric Out << "' call may invalidate the result of the previous " << '\''; 134349cc55cSDimitry Andric FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); 135349cc55cSDimitry Andric Out << '\''; 136349cc55cSDimitry Andric }); 137349cc55cSDimitry Andric } 138349cc55cSDimitry Andric 139349cc55cSDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 140349cc55cSDimitry Andric const auto *CE = cast<CallExpr>(Call.getOriginExpr()); 141349cc55cSDimitry Andric 142349cc55cSDimitry Andric // Function call will return a pointer to the new symbolic region. 143349cc55cSDimitry Andric DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal( 144349cc55cSDimitry Andric CE, LCtx, CE->getType(), C.blockCount()); 145349cc55cSDimitry Andric State = State->BindExpr(CE, LCtx, RetVal); 146349cc55cSDimitry Andric 147349cc55cSDimitry Andric // Remember to this region. 148349cc55cSDimitry Andric const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion()); 149349cc55cSDimitry Andric const MemRegion *MR = 150349cc55cSDimitry Andric const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion()); 151349cc55cSDimitry Andric State = State->set<PreviousCallResultMap>(FD, MR); 152349cc55cSDimitry Andric 153349cc55cSDimitry Andric ExplodedNode *Node = C.addTransition(State, Note); 154349cc55cSDimitry Andric const NoteTag *PreviousCallNote = 155349cc55cSDimitry Andric C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { 156349cc55cSDimitry Andric if (!BR.isInteresting(MR)) 157349cc55cSDimitry Andric return; 158349cc55cSDimitry Andric Out << '\'' << "'previous function call was here" << '\''; 159349cc55cSDimitry Andric }); 160349cc55cSDimitry Andric 161349cc55cSDimitry Andric C.addTransition(State, Node, PreviousCallNote); 162349cc55cSDimitry Andric } 163349cc55cSDimitry Andric 164349cc55cSDimitry Andric // TODO: This seems really ugly. Simplify this. 165349cc55cSDimitry Andric static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State, 166349cc55cSDimitry Andric const MemRegion *Reg) { 167349cc55cSDimitry Andric while (Reg) { 168349cc55cSDimitry Andric if (State->contains<InvalidMemoryRegions>(Reg)) 169349cc55cSDimitry Andric return Reg; 170349cc55cSDimitry Andric const auto *SymBase = Reg->getSymbolicBase(); 171349cc55cSDimitry Andric if (!SymBase) 172349cc55cSDimitry Andric break; 173349cc55cSDimitry Andric const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol()); 174349cc55cSDimitry Andric if (!SRV) 175349cc55cSDimitry Andric break; 176349cc55cSDimitry Andric Reg = SRV->getRegion(); 177349cc55cSDimitry Andric if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion())) 178349cc55cSDimitry Andric Reg = VarReg; 179349cc55cSDimitry Andric } 180349cc55cSDimitry Andric return nullptr; 181349cc55cSDimitry Andric } 182349cc55cSDimitry Andric 183349cc55cSDimitry Andric // Handle functions in EnvpInvalidatingFunctions, that invalidate environment 184349cc55cSDimitry Andric // pointer from 'main()' Also, check if invalidated region is passed to a 185349cc55cSDimitry Andric // function call as an argument. 186349cc55cSDimitry Andric void InvalidPtrChecker::checkPostCall(const CallEvent &Call, 187349cc55cSDimitry Andric CheckerContext &C) const { 188349cc55cSDimitry Andric // Check if function invalidates 'envp' argument of 'main' 189349cc55cSDimitry Andric if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call)) 190349cc55cSDimitry Andric (this->**Handler)(Call, C); 191349cc55cSDimitry Andric 192349cc55cSDimitry Andric // Check if function invalidates the result of previous call 193349cc55cSDimitry Andric if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call)) 194349cc55cSDimitry Andric (this->**Handler)(Call, C); 195349cc55cSDimitry Andric 196349cc55cSDimitry Andric // Check if one of the arguments of the function call is invalidated 197349cc55cSDimitry Andric 198349cc55cSDimitry Andric // If call was inlined, don't report invalidated argument 199349cc55cSDimitry Andric if (C.wasInlined) 200349cc55cSDimitry Andric return; 201349cc55cSDimitry Andric 202349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 203349cc55cSDimitry Andric 204349cc55cSDimitry Andric for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) { 205349cc55cSDimitry Andric 206349cc55cSDimitry Andric if (const auto *SR = dyn_cast_or_null<SymbolicRegion>( 207349cc55cSDimitry Andric Call.getArgSVal(I).getAsRegion())) { 208349cc55cSDimitry Andric if (const MemRegion *InvalidatedSymbolicBase = 209349cc55cSDimitry Andric findInvalidatedSymbolicBase(State, SR)) { 210349cc55cSDimitry Andric ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); 211349cc55cSDimitry Andric if (!ErrorNode) 212349cc55cSDimitry Andric return; 213349cc55cSDimitry Andric 214349cc55cSDimitry Andric SmallString<256> Msg; 215349cc55cSDimitry Andric llvm::raw_svector_ostream Out(Msg); 216349cc55cSDimitry Andric Out << "use of invalidated pointer '"; 217349cc55cSDimitry Andric Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr, 218349cc55cSDimitry Andric C.getASTContext().getPrintingPolicy()); 219349cc55cSDimitry Andric Out << "' in a function call"; 220349cc55cSDimitry Andric 221349cc55cSDimitry Andric auto Report = 222349cc55cSDimitry Andric std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode); 223349cc55cSDimitry Andric Report->markInteresting(InvalidatedSymbolicBase); 224349cc55cSDimitry Andric Report->addRange(Call.getArgSourceRange(I)); 225349cc55cSDimitry Andric C.emitReport(std::move(Report)); 226349cc55cSDimitry Andric } 227349cc55cSDimitry Andric } 228349cc55cSDimitry Andric } 229349cc55cSDimitry Andric } 230349cc55cSDimitry Andric 231349cc55cSDimitry Andric // Obtain the environment pointer from 'main()', if present. 232349cc55cSDimitry Andric void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const { 233349cc55cSDimitry Andric if (!C.inTopFrame()) 234349cc55cSDimitry Andric return; 235349cc55cSDimitry Andric 236349cc55cSDimitry Andric const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl()); 237349cc55cSDimitry Andric if (!FD || FD->param_size() != 3 || !FD->isMain()) 238349cc55cSDimitry Andric return; 239349cc55cSDimitry Andric 240349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 241349cc55cSDimitry Andric const MemRegion *EnvpReg = 242349cc55cSDimitry Andric State->getRegion(FD->parameters()[2], C.getLocationContext()); 243349cc55cSDimitry Andric 244349cc55cSDimitry Andric // Save the memory region pointed by the environment pointer parameter of 245349cc55cSDimitry Andric // 'main'. 24681ad6265SDimitry Andric C.addTransition(State->set<EnvPtrRegion>(EnvpReg)); 247349cc55cSDimitry Andric } 248349cc55cSDimitry Andric 249349cc55cSDimitry Andric // Check if invalidated region is being dereferenced. 250349cc55cSDimitry Andric void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S, 251349cc55cSDimitry Andric CheckerContext &C) const { 252349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 253349cc55cSDimitry Andric 254349cc55cSDimitry Andric // Ignore memory operations involving 'non-invalidated' locations. 255349cc55cSDimitry Andric const MemRegion *InvalidatedSymbolicBase = 256349cc55cSDimitry Andric findInvalidatedSymbolicBase(State, Loc.getAsRegion()); 257349cc55cSDimitry Andric if (!InvalidatedSymbolicBase) 258349cc55cSDimitry Andric return; 259349cc55cSDimitry Andric 260349cc55cSDimitry Andric ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); 261349cc55cSDimitry Andric if (!ErrorNode) 262349cc55cSDimitry Andric return; 263349cc55cSDimitry Andric 264349cc55cSDimitry Andric auto Report = std::make_unique<PathSensitiveBugReport>( 265349cc55cSDimitry Andric BT, "dereferencing an invalid pointer", ErrorNode); 266349cc55cSDimitry Andric Report->markInteresting(InvalidatedSymbolicBase); 267349cc55cSDimitry Andric C.emitReport(std::move(Report)); 268349cc55cSDimitry Andric } 269349cc55cSDimitry Andric 270349cc55cSDimitry Andric void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { 271349cc55cSDimitry Andric Mgr.registerChecker<InvalidPtrChecker>(); 272349cc55cSDimitry Andric } 273349cc55cSDimitry Andric 274349cc55cSDimitry Andric bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { 275349cc55cSDimitry Andric return true; 276349cc55cSDimitry Andric } 277