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