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