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