1*349cc55cSDimitry Andric //== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=// 2*349cc55cSDimitry Andric // 3*349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*349cc55cSDimitry Andric // 7*349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 8*349cc55cSDimitry Andric // 9*349cc55cSDimitry Andric // This file defines InvalidPtrChecker which finds usages of possibly 10*349cc55cSDimitry Andric // invalidated pointer. 11*349cc55cSDimitry Andric // CERT SEI Rules ENV31-C and ENV34-C 12*349cc55cSDimitry Andric // For more information see: 13*349cc55cSDimitry Andric // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ 14*349cc55cSDimitry Andric // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ 15*349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 16*349cc55cSDimitry Andric 17*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 20*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 22*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 23*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 24*349cc55cSDimitry Andric 25*349cc55cSDimitry Andric using namespace clang; 26*349cc55cSDimitry Andric using namespace ento; 27*349cc55cSDimitry Andric 28*349cc55cSDimitry Andric namespace { 29*349cc55cSDimitry Andric 30*349cc55cSDimitry Andric class InvalidPtrChecker 31*349cc55cSDimitry Andric : public Checker<check::Location, check::BeginFunction, check::PostCall> { 32*349cc55cSDimitry Andric private: 33*349cc55cSDimitry Andric BugType BT{this, "Use of invalidated pointer", categories::MemoryError}; 34*349cc55cSDimitry Andric 35*349cc55cSDimitry Andric void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const; 36*349cc55cSDimitry Andric 37*349cc55cSDimitry Andric using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call, 38*349cc55cSDimitry Andric CheckerContext &C) const; 39*349cc55cSDimitry Andric 40*349cc55cSDimitry Andric // SEI CERT ENV31-C 41*349cc55cSDimitry Andric const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = { 42*349cc55cSDimitry Andric {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, 43*349cc55cSDimitry Andric {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, 44*349cc55cSDimitry Andric {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, 45*349cc55cSDimitry Andric {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, 46*349cc55cSDimitry Andric {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, 47*349cc55cSDimitry Andric }; 48*349cc55cSDimitry Andric 49*349cc55cSDimitry Andric void postPreviousReturnInvalidatingCall(const CallEvent &Call, 50*349cc55cSDimitry Andric CheckerContext &C) const; 51*349cc55cSDimitry Andric 52*349cc55cSDimitry Andric // SEI CERT ENV34-C 53*349cc55cSDimitry Andric const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = { 54*349cc55cSDimitry Andric {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 55*349cc55cSDimitry Andric {{"setlocale", 2}, 56*349cc55cSDimitry Andric &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 57*349cc55cSDimitry Andric {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 58*349cc55cSDimitry Andric {{"localeconv", 0}, 59*349cc55cSDimitry Andric &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 60*349cc55cSDimitry Andric {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 61*349cc55cSDimitry Andric }; 62*349cc55cSDimitry Andric 63*349cc55cSDimitry Andric public: 64*349cc55cSDimitry Andric // Obtain the environment pointer from 'main()' (if present). 65*349cc55cSDimitry Andric void checkBeginFunction(CheckerContext &C) const; 66*349cc55cSDimitry Andric 67*349cc55cSDimitry Andric // Handle functions in EnvpInvalidatingFunctions, that invalidate environment 68*349cc55cSDimitry Andric // pointer from 'main()' 69*349cc55cSDimitry Andric // Handle functions in PreviousCallInvalidatingFunctions. 70*349cc55cSDimitry Andric // Also, check if invalidated region is passed to a 71*349cc55cSDimitry Andric // conservatively evaluated function call as an argument. 72*349cc55cSDimitry Andric void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 73*349cc55cSDimitry Andric 74*349cc55cSDimitry Andric // Check if invalidated region is being dereferenced. 75*349cc55cSDimitry Andric void checkLocation(SVal l, bool isLoad, const Stmt *S, 76*349cc55cSDimitry Andric CheckerContext &C) const; 77*349cc55cSDimitry Andric }; 78*349cc55cSDimitry Andric 79*349cc55cSDimitry Andric } // namespace 80*349cc55cSDimitry Andric 81*349cc55cSDimitry Andric // Set of memory regions that were invalidated 82*349cc55cSDimitry Andric REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *) 83*349cc55cSDimitry Andric 84*349cc55cSDimitry Andric // Stores the region of the environment pointer of 'main' (if present). 85*349cc55cSDimitry Andric // Note: This pointer has type 'const MemRegion *', however the trait is only 86*349cc55cSDimitry Andric // specialized to 'const void*' and 'void*' 87*349cc55cSDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *) 88*349cc55cSDimitry Andric 89*349cc55cSDimitry Andric // Stores key-value pairs, where key is function declaration and value is 90*349cc55cSDimitry Andric // pointer to memory region returned by previous call of this function 91*349cc55cSDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, 92*349cc55cSDimitry Andric const MemRegion *) 93*349cc55cSDimitry Andric 94*349cc55cSDimitry Andric void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call, 95*349cc55cSDimitry Andric CheckerContext &C) const { 96*349cc55cSDimitry Andric StringRef FunctionName = Call.getCalleeIdentifier()->getName(); 97*349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 98*349cc55cSDimitry Andric const auto *Reg = State->get<EnvPtrRegion>(); 99*349cc55cSDimitry Andric if (!Reg) 100*349cc55cSDimitry Andric return; 101*349cc55cSDimitry Andric const auto *SymbolicEnvPtrRegion = 102*349cc55cSDimitry Andric reinterpret_cast<const MemRegion *>(const_cast<const void *>(Reg)); 103*349cc55cSDimitry Andric 104*349cc55cSDimitry Andric State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion); 105*349cc55cSDimitry Andric 106*349cc55cSDimitry Andric const NoteTag *Note = 107*349cc55cSDimitry Andric C.getNoteTag([SymbolicEnvPtrRegion, FunctionName]( 108*349cc55cSDimitry Andric PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { 109*349cc55cSDimitry Andric if (!BR.isInteresting(SymbolicEnvPtrRegion)) 110*349cc55cSDimitry Andric return; 111*349cc55cSDimitry Andric Out << '\'' << FunctionName 112*349cc55cSDimitry Andric << "' call may invalidate the environment parameter of 'main'"; 113*349cc55cSDimitry Andric }); 114*349cc55cSDimitry Andric 115*349cc55cSDimitry Andric C.addTransition(State, Note); 116*349cc55cSDimitry Andric } 117*349cc55cSDimitry Andric 118*349cc55cSDimitry Andric void InvalidPtrChecker::postPreviousReturnInvalidatingCall( 119*349cc55cSDimitry Andric const CallEvent &Call, CheckerContext &C) const { 120*349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 121*349cc55cSDimitry Andric 122*349cc55cSDimitry Andric const NoteTag *Note = nullptr; 123*349cc55cSDimitry Andric const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 124*349cc55cSDimitry Andric // Invalidate the region of the previously returned pointer - if there was 125*349cc55cSDimitry Andric // one. 126*349cc55cSDimitry Andric if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) { 127*349cc55cSDimitry Andric const MemRegion *PrevReg = *Reg; 128*349cc55cSDimitry Andric State = State->add<InvalidMemoryRegions>(PrevReg); 129*349cc55cSDimitry Andric Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR, 130*349cc55cSDimitry Andric llvm::raw_ostream &Out) { 131*349cc55cSDimitry Andric if (!BR.isInteresting(PrevReg)) 132*349cc55cSDimitry Andric return; 133*349cc55cSDimitry Andric Out << '\''; 134*349cc55cSDimitry Andric FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); 135*349cc55cSDimitry Andric Out << "' call may invalidate the the result of the previous " << '\''; 136*349cc55cSDimitry Andric FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); 137*349cc55cSDimitry Andric Out << '\''; 138*349cc55cSDimitry Andric }); 139*349cc55cSDimitry Andric } 140*349cc55cSDimitry Andric 141*349cc55cSDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 142*349cc55cSDimitry Andric const auto *CE = cast<CallExpr>(Call.getOriginExpr()); 143*349cc55cSDimitry Andric 144*349cc55cSDimitry Andric // Function call will return a pointer to the new symbolic region. 145*349cc55cSDimitry Andric DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal( 146*349cc55cSDimitry Andric CE, LCtx, CE->getType(), C.blockCount()); 147*349cc55cSDimitry Andric State = State->BindExpr(CE, LCtx, RetVal); 148*349cc55cSDimitry Andric 149*349cc55cSDimitry Andric // Remember to this region. 150*349cc55cSDimitry Andric const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion()); 151*349cc55cSDimitry Andric const MemRegion *MR = 152*349cc55cSDimitry Andric const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion()); 153*349cc55cSDimitry Andric State = State->set<PreviousCallResultMap>(FD, MR); 154*349cc55cSDimitry Andric 155*349cc55cSDimitry Andric ExplodedNode *Node = C.addTransition(State, Note); 156*349cc55cSDimitry Andric const NoteTag *PreviousCallNote = 157*349cc55cSDimitry Andric C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { 158*349cc55cSDimitry Andric if (!BR.isInteresting(MR)) 159*349cc55cSDimitry Andric return; 160*349cc55cSDimitry Andric Out << '\'' << "'previous function call was here" << '\''; 161*349cc55cSDimitry Andric }); 162*349cc55cSDimitry Andric 163*349cc55cSDimitry Andric C.addTransition(State, Node, PreviousCallNote); 164*349cc55cSDimitry Andric } 165*349cc55cSDimitry Andric 166*349cc55cSDimitry Andric // TODO: This seems really ugly. Simplify this. 167*349cc55cSDimitry Andric static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State, 168*349cc55cSDimitry Andric const MemRegion *Reg) { 169*349cc55cSDimitry Andric while (Reg) { 170*349cc55cSDimitry Andric if (State->contains<InvalidMemoryRegions>(Reg)) 171*349cc55cSDimitry Andric return Reg; 172*349cc55cSDimitry Andric const auto *SymBase = Reg->getSymbolicBase(); 173*349cc55cSDimitry Andric if (!SymBase) 174*349cc55cSDimitry Andric break; 175*349cc55cSDimitry Andric const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol()); 176*349cc55cSDimitry Andric if (!SRV) 177*349cc55cSDimitry Andric break; 178*349cc55cSDimitry Andric Reg = SRV->getRegion(); 179*349cc55cSDimitry Andric if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion())) 180*349cc55cSDimitry Andric Reg = VarReg; 181*349cc55cSDimitry Andric } 182*349cc55cSDimitry Andric return nullptr; 183*349cc55cSDimitry Andric } 184*349cc55cSDimitry Andric 185*349cc55cSDimitry Andric // Handle functions in EnvpInvalidatingFunctions, that invalidate environment 186*349cc55cSDimitry Andric // pointer from 'main()' Also, check if invalidated region is passed to a 187*349cc55cSDimitry Andric // function call as an argument. 188*349cc55cSDimitry Andric void InvalidPtrChecker::checkPostCall(const CallEvent &Call, 189*349cc55cSDimitry Andric CheckerContext &C) const { 190*349cc55cSDimitry Andric // Check if function invalidates 'envp' argument of 'main' 191*349cc55cSDimitry Andric if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call)) 192*349cc55cSDimitry Andric (this->**Handler)(Call, C); 193*349cc55cSDimitry Andric 194*349cc55cSDimitry Andric // Check if function invalidates the result of previous call 195*349cc55cSDimitry Andric if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call)) 196*349cc55cSDimitry Andric (this->**Handler)(Call, C); 197*349cc55cSDimitry Andric 198*349cc55cSDimitry Andric // Check if one of the arguments of the function call is invalidated 199*349cc55cSDimitry Andric 200*349cc55cSDimitry Andric // If call was inlined, don't report invalidated argument 201*349cc55cSDimitry Andric if (C.wasInlined) 202*349cc55cSDimitry Andric return; 203*349cc55cSDimitry Andric 204*349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 205*349cc55cSDimitry Andric 206*349cc55cSDimitry Andric for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) { 207*349cc55cSDimitry Andric 208*349cc55cSDimitry Andric if (const auto *SR = dyn_cast_or_null<SymbolicRegion>( 209*349cc55cSDimitry Andric Call.getArgSVal(I).getAsRegion())) { 210*349cc55cSDimitry Andric if (const MemRegion *InvalidatedSymbolicBase = 211*349cc55cSDimitry Andric findInvalidatedSymbolicBase(State, SR)) { 212*349cc55cSDimitry Andric ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); 213*349cc55cSDimitry Andric if (!ErrorNode) 214*349cc55cSDimitry Andric return; 215*349cc55cSDimitry Andric 216*349cc55cSDimitry Andric SmallString<256> Msg; 217*349cc55cSDimitry Andric llvm::raw_svector_ostream Out(Msg); 218*349cc55cSDimitry Andric Out << "use of invalidated pointer '"; 219*349cc55cSDimitry Andric Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr, 220*349cc55cSDimitry Andric C.getASTContext().getPrintingPolicy()); 221*349cc55cSDimitry Andric Out << "' in a function call"; 222*349cc55cSDimitry Andric 223*349cc55cSDimitry Andric auto Report = 224*349cc55cSDimitry Andric std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode); 225*349cc55cSDimitry Andric Report->markInteresting(InvalidatedSymbolicBase); 226*349cc55cSDimitry Andric Report->addRange(Call.getArgSourceRange(I)); 227*349cc55cSDimitry Andric C.emitReport(std::move(Report)); 228*349cc55cSDimitry Andric } 229*349cc55cSDimitry Andric } 230*349cc55cSDimitry Andric } 231*349cc55cSDimitry Andric } 232*349cc55cSDimitry Andric 233*349cc55cSDimitry Andric // Obtain the environment pointer from 'main()', if present. 234*349cc55cSDimitry Andric void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const { 235*349cc55cSDimitry Andric if (!C.inTopFrame()) 236*349cc55cSDimitry Andric return; 237*349cc55cSDimitry Andric 238*349cc55cSDimitry Andric const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl()); 239*349cc55cSDimitry Andric if (!FD || FD->param_size() != 3 || !FD->isMain()) 240*349cc55cSDimitry Andric return; 241*349cc55cSDimitry Andric 242*349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 243*349cc55cSDimitry Andric const MemRegion *EnvpReg = 244*349cc55cSDimitry Andric State->getRegion(FD->parameters()[2], C.getLocationContext()); 245*349cc55cSDimitry Andric 246*349cc55cSDimitry Andric // Save the memory region pointed by the environment pointer parameter of 247*349cc55cSDimitry Andric // 'main'. 248*349cc55cSDimitry Andric State = State->set<EnvPtrRegion>( 249*349cc55cSDimitry Andric reinterpret_cast<void *>(const_cast<MemRegion *>(EnvpReg))); 250*349cc55cSDimitry Andric C.addTransition(State); 251*349cc55cSDimitry Andric } 252*349cc55cSDimitry Andric 253*349cc55cSDimitry Andric // Check if invalidated region is being dereferenced. 254*349cc55cSDimitry Andric void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S, 255*349cc55cSDimitry Andric CheckerContext &C) const { 256*349cc55cSDimitry Andric ProgramStateRef State = C.getState(); 257*349cc55cSDimitry Andric 258*349cc55cSDimitry Andric // Ignore memory operations involving 'non-invalidated' locations. 259*349cc55cSDimitry Andric const MemRegion *InvalidatedSymbolicBase = 260*349cc55cSDimitry Andric findInvalidatedSymbolicBase(State, Loc.getAsRegion()); 261*349cc55cSDimitry Andric if (!InvalidatedSymbolicBase) 262*349cc55cSDimitry Andric return; 263*349cc55cSDimitry Andric 264*349cc55cSDimitry Andric ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); 265*349cc55cSDimitry Andric if (!ErrorNode) 266*349cc55cSDimitry Andric return; 267*349cc55cSDimitry Andric 268*349cc55cSDimitry Andric auto Report = std::make_unique<PathSensitiveBugReport>( 269*349cc55cSDimitry Andric BT, "dereferencing an invalid pointer", ErrorNode); 270*349cc55cSDimitry Andric Report->markInteresting(InvalidatedSymbolicBase); 271*349cc55cSDimitry Andric C.emitReport(std::move(Report)); 272*349cc55cSDimitry Andric } 273*349cc55cSDimitry Andric 274*349cc55cSDimitry Andric void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { 275*349cc55cSDimitry Andric Mgr.registerChecker<InvalidPtrChecker>(); 276*349cc55cSDimitry Andric } 277*349cc55cSDimitry Andric 278*349cc55cSDimitry Andric bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { 279*349cc55cSDimitry Andric return true; 280*349cc55cSDimitry Andric } 281