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{{"getenv"}, 1}; 52 53 const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = { 54 {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, 55 {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, 56 {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, 57 {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, 58 {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, 59 }; 60 61 void postPreviousReturnInvalidatingCall(const CallEvent &Call, 62 CheckerContext &C) const; 63 64 // SEI CERT ENV34-C 65 const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = { 66 {{{"setlocale"}, 2}, 67 &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 68 {{{"strerror"}, 1}, 69 &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 70 {{{"localeconv"}, 0}, 71 &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 72 {{{"asctime"}, 1}, 73 &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, 74 }; 75 76 // The private members of this checker corresponding to commandline options 77 // are set in this function. 78 friend void ento::registerInvalidPtrChecker(CheckerManager &); 79 80 public: 81 // Obtain the environment pointer from 'main()' (if present). 82 void checkBeginFunction(CheckerContext &C) const; 83 84 // Handle functions in EnvpInvalidatingFunctions, that invalidate environment 85 // pointer from 'main()' 86 // Handle functions in PreviousCallInvalidatingFunctions. 87 // Also, check if invalidated region is passed to a 88 // conservatively evaluated function call as an argument. 89 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 90 91 // Check if invalidated region is being dereferenced. 92 void checkLocation(SVal l, bool isLoad, const Stmt *S, 93 CheckerContext &C) const; 94 95 private: 96 const NoteTag *createEnvInvalidationNote(CheckerContext &C, 97 ProgramStateRef State, 98 StringRef FunctionName) const; 99 }; 100 101 } // namespace 102 103 // Set of memory regions that were invalidated 104 REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *) 105 106 // Stores the region of the environment pointer of 'main' (if present). 107 REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion, const MemRegion *) 108 109 // Stores the regions of environments returned by getenv calls. 110 REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions, const MemRegion *) 111 112 // Stores key-value pairs, where key is function declaration and value is 113 // pointer to memory region returned by previous call of this function 114 REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, 115 const MemRegion *) 116 117 const NoteTag *InvalidPtrChecker::createEnvInvalidationNote( 118 CheckerContext &C, ProgramStateRef State, StringRef FunctionName) const { 119 120 const MemRegion *MainRegion = State->get<MainEnvPtrRegion>(); 121 const auto GetenvRegions = State->get<GetenvEnvPtrRegions>(); 122 123 return C.getNoteTag([this, MainRegion, GetenvRegions, 124 FunctionName = std::string{FunctionName}]( 125 PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { 126 // Only handle the BugType of this checker. 127 if (&BR.getBugType() != &InvalidPtrBugType) 128 return; 129 130 // Mark all regions that were interesting before as NOT interesting now 131 // to avoid extra notes coming from invalidation points higher up the 132 // bugpath. This ensures that only the last invalidation point is marked 133 // with a note tag. 134 llvm::SmallVector<std::string, 2> InvalidLocationNames; 135 if (BR.isInteresting(MainRegion)) { 136 BR.markNotInteresting(MainRegion); 137 InvalidLocationNames.push_back("the environment parameter of 'main'"); 138 } 139 bool InterestingGetenvFound = false; 140 for (const MemRegion *MR : GetenvRegions) { 141 if (BR.isInteresting(MR)) { 142 BR.markNotInteresting(MR); 143 if (!InterestingGetenvFound) { 144 InterestingGetenvFound = true; 145 InvalidLocationNames.push_back( 146 "the environment returned by 'getenv'"); 147 } 148 } 149 } 150 151 // Emit note tag message. 152 if (InvalidLocationNames.size() >= 1) 153 Out << '\'' << FunctionName << "' call may invalidate " 154 << InvalidLocationNames[0]; 155 if (InvalidLocationNames.size() == 2) 156 Out << ", and " << InvalidLocationNames[1]; 157 }); 158 } 159 160 void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call, 161 CheckerContext &C) const { 162 // This callevent invalidates all previously generated pointers to the 163 // environment. 164 ProgramStateRef State = C.getState(); 165 if (const MemRegion *MainEnvPtr = State->get<MainEnvPtrRegion>()) 166 State = State->add<InvalidMemoryRegions>(MainEnvPtr); 167 for (const MemRegion *EnvPtr : State->get<GetenvEnvPtrRegions>()) 168 State = State->add<InvalidMemoryRegions>(EnvPtr); 169 170 StringRef FunctionName = Call.getCalleeIdentifier()->getName(); 171 const NoteTag *InvalidationNote = 172 createEnvInvalidationNote(C, State, FunctionName); 173 174 C.addTransition(State, InvalidationNote); 175 } 176 177 void InvalidPtrChecker::postPreviousReturnInvalidatingCall( 178 const CallEvent &Call, CheckerContext &C) const { 179 ProgramStateRef State = C.getState(); 180 181 const NoteTag *Note = nullptr; 182 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 183 // Invalidate the region of the previously returned pointer - if there was 184 // one. 185 if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) { 186 const MemRegion *PrevReg = *Reg; 187 State = State->add<InvalidMemoryRegions>(PrevReg); 188 Note = C.getNoteTag([this, PrevReg, FD](PathSensitiveBugReport &BR, 189 llvm::raw_ostream &Out) { 190 if (!BR.isInteresting(PrevReg) || &BR.getBugType() != &InvalidPtrBugType) 191 return; 192 Out << '\''; 193 FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); 194 Out << "' call may invalidate the result of the previous " << '\''; 195 FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); 196 Out << '\''; 197 }); 198 } 199 200 const LocationContext *LCtx = C.getLocationContext(); 201 const auto *CE = cast<CallExpr>(Call.getOriginExpr()); 202 203 // Function call will return a pointer to the new symbolic region. 204 DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal( 205 CE, LCtx, CE->getType(), C.blockCount()); 206 State = State->BindExpr(CE, LCtx, RetVal); 207 208 // Remember to this region. 209 const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion()); 210 const MemRegion *MR = SymRegOfRetVal->getBaseRegion(); 211 State = State->set<PreviousCallResultMap>(FD, MR); 212 213 ExplodedNode *Node = C.addTransition(State, Note); 214 const NoteTag *PreviousCallNote = C.getNoteTag( 215 [this, MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { 216 if (!BR.isInteresting(MR) || &BR.getBugType() != &InvalidPtrBugType) 217 return; 218 Out << "previous function call was here"; 219 }); 220 221 C.addTransition(State, Node, PreviousCallNote); 222 } 223 224 // TODO: This seems really ugly. Simplify this. 225 static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State, 226 const MemRegion *Reg) { 227 while (Reg) { 228 if (State->contains<InvalidMemoryRegions>(Reg)) 229 return Reg; 230 const auto *SymBase = Reg->getSymbolicBase(); 231 if (!SymBase) 232 break; 233 const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol()); 234 if (!SRV) 235 break; 236 Reg = SRV->getRegion(); 237 if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion())) 238 Reg = VarReg; 239 } 240 return nullptr; 241 } 242 243 // Handle functions in EnvpInvalidatingFunctions, that invalidate environment 244 // pointer from 'main()' Also, check if invalidated region is passed to a 245 // function call as an argument. 246 void InvalidPtrChecker::checkPostCall(const CallEvent &Call, 247 CheckerContext &C) const { 248 249 ProgramStateRef State = C.getState(); 250 251 // Model 'getenv' calls 252 if (GetEnvCall.matches(Call)) { 253 const MemRegion *Region = Call.getReturnValue().getAsRegion(); 254 if (Region) { 255 State = State->add<GetenvEnvPtrRegions>(Region); 256 C.addTransition(State); 257 } 258 } 259 260 // Check if function invalidates 'envp' argument of 'main' 261 if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call)) 262 (this->**Handler)(Call, C); 263 264 // Check if function invalidates the result of previous call 265 if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call)) 266 (this->**Handler)(Call, C); 267 268 // If pedantic mode is on, regard 'getenv' calls invalidating as well 269 if (InvalidatingGetEnv && GetEnvCall.matches(Call)) 270 postPreviousReturnInvalidatingCall(Call, C); 271 272 // Check if one of the arguments of the function call is invalidated 273 274 // If call was inlined, don't report invalidated argument 275 if (C.wasInlined) 276 return; 277 278 for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) { 279 280 if (const auto *SR = dyn_cast_or_null<SymbolicRegion>( 281 Call.getArgSVal(I).getAsRegion())) { 282 if (const MemRegion *InvalidatedSymbolicBase = 283 findInvalidatedSymbolicBase(State, SR)) { 284 ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); 285 if (!ErrorNode) 286 return; 287 288 SmallString<256> Msg; 289 llvm::raw_svector_ostream Out(Msg); 290 Out << "use of invalidated pointer '"; 291 Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr, 292 C.getASTContext().getPrintingPolicy()); 293 Out << "' in a function call"; 294 295 auto Report = std::make_unique<PathSensitiveBugReport>( 296 InvalidPtrBugType, Out.str(), ErrorNode); 297 Report->markInteresting(InvalidatedSymbolicBase); 298 Report->addRange(Call.getArgSourceRange(I)); 299 C.emitReport(std::move(Report)); 300 } 301 } 302 } 303 } 304 305 // Obtain the environment pointer from 'main()', if present. 306 void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const { 307 if (!C.inTopFrame()) 308 return; 309 310 const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl()); 311 if (!FD || FD->param_size() != 3 || !FD->isMain()) 312 return; 313 314 ProgramStateRef State = C.getState(); 315 const MemRegion *EnvpReg = 316 State->getRegion(FD->parameters()[2], C.getLocationContext()); 317 318 // Save the memory region pointed by the environment pointer parameter of 319 // 'main'. 320 C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg)); 321 } 322 323 // Check if invalidated region is being dereferenced. 324 void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S, 325 CheckerContext &C) const { 326 ProgramStateRef State = C.getState(); 327 328 // Ignore memory operations involving 'non-invalidated' locations. 329 const MemRegion *InvalidatedSymbolicBase = 330 findInvalidatedSymbolicBase(State, Loc.getAsRegion()); 331 if (!InvalidatedSymbolicBase) 332 return; 333 334 ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); 335 if (!ErrorNode) 336 return; 337 338 auto Report = std::make_unique<PathSensitiveBugReport>( 339 InvalidPtrBugType, "dereferencing an invalid pointer", ErrorNode); 340 Report->markInteresting(InvalidatedSymbolicBase); 341 C.emitReport(std::move(Report)); 342 } 343 344 void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { 345 auto *Checker = Mgr.registerChecker<InvalidPtrChecker>(); 346 Checker->InvalidatingGetEnv = 347 Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, 348 "InvalidatingGetEnv"); 349 } 350 351 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { 352 return true; 353 } 354