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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines InvalidPtrChecker which finds usages of possibly 10 // invalidated pointer. 11 // CERT SEI Rules ENV31-C and ENV34-C 12 // For more information see: 13 // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ 14 // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ 15 //===----------------------------------------------------------------------===// 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 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 30 class InvalidPtrChecker 31 : public Checker<check::Location, check::BeginFunction, check::PostCall> { 32 private: 33 // For accurate emission of NoteTags, the BugType of this checker should have 34 // 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-C 44 45 // If set to true, consider getenv calls as invalidating operations on the 46 // environment variable buffer. This is implied in the standard, but in 47 // 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-C 70 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 options 82 // are set in this function. 83 friend void ento::registerInvalidPtrChecker(CheckerManager &); 84 85 public: 86 // Obtain the environment pointer from 'main()' (if present). 87 void checkBeginFunction(CheckerContext &C) const; 88 89 // Handle functions in EnvpInvalidatingFunctions, that invalidate environment 90 // pointer from 'main()' 91 // Handle functions in PreviousCallInvalidatingFunctions. 92 // Also, check if invalidated region is passed to a 93 // 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 100 private: 101 const NoteTag *createEnvInvalidationNote(CheckerContext &C, 102 ProgramStateRef State, 103 StringRef FunctionName) const; 104 }; 105 106 } // namespace 107 108 // Set of memory regions that were invalidated 109 REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *) 110 111 // Stores the region of the environment pointer of 'main' (if present). 112 REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion, const MemRegion *) 113 114 // Stores the regions of environments returned by getenv calls. 115 REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions, const MemRegion *) 116 117 // Stores key-value pairs, where key is function declaration and value is 118 // pointer to memory region returned by previous call of this function 119 REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, 120 const MemRegion *) 121 122 const 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 now 136 // to avoid extra notes coming from invalidation points higher up the 137 // bugpath. This ensures that only the last invalidation point is marked 138 // 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 165 void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call, 166 CheckerContext &C) const { 167 // This callevent invalidates all previously generated pointers to the 168 // 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 182 void 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 was 189 // 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 = C.getSValBuilder().conjureSymbolVal( 210 CE, LCtx, CE->getType(), 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. 234 static 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 environment 253 // pointer from 'main()' Also, check if invalidated region is passed to a 254 // function call as an argument. 255 void InvalidPtrChecker::checkPostCall(const CallEvent &Call, 256 CheckerContext &C) const { 257 258 ProgramStateRef State = C.getState(); 259 260 // Model 'getenv' calls 261 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 call 274 if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call)) 275 (this->**Handler)(Call, C); 276 277 // If pedantic mode is on, regard 'getenv' calls invalidating as well 278 if (InvalidatingGetEnv && GetEnvCall.matches(Call)) 279 postPreviousReturnInvalidatingCall(Call, C); 280 281 // Check if one of the arguments of the function call is invalidated 282 283 // If call was inlined, don't report invalidated argument 284 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. 315 void 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 of 328 // 'main'. 329 C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg)); 330 } 331 332 // Check if invalidated region is being dereferenced. 333 void 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 353 void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { 354 auto *Checker = Mgr.registerChecker<InvalidPtrChecker>(); 355 Checker->InvalidatingGetEnv = 356 Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, 357 "InvalidatingGetEnv"); 358 } 359 360 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { 361 return true; 362 } 363