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