1 //=== InnerPointerChecker.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 a check that marks a raw pointer to a C++ container's 10 // inner buffer released when the object is destroyed. This information can 11 // be used by MallocChecker to detect use-after-free problems. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "AllocationState.h" 16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 17 #include "InterCheckerAPI.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" 20 #include "clang/StaticAnalyzer/Core/Checker.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 // Associate container objects with a set of raw pointer symbols. 28 REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef) 29 REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet) 30 31 32 namespace { 33 34 class InnerPointerChecker 35 : public Checker<check::DeadSymbols, check::PostCall> { 36 37 CallDescription AppendFn, AssignFn, AddressofFn, ClearFn, CStrFn, DataFn, 38 DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn, ReplaceFn, 39 ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn; 40 41 public: 42 class InnerPointerBRVisitor : public BugReporterVisitor { 43 SymbolRef PtrToBuf; 44 45 public: 46 InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {} 47 48 static void *getTag() { 49 static int Tag = 0; 50 return &Tag; 51 } 52 53 void Profile(llvm::FoldingSetNodeID &ID) const override { 54 ID.AddPointer(getTag()); 55 } 56 57 virtual PathDiagnosticPieceRef 58 VisitNode(const ExplodedNode *N, BugReporterContext &BRC, 59 PathSensitiveBugReport &BR) override; 60 61 // FIXME: Scan the map once in the visitor's constructor and do a direct 62 // lookup by region. 63 bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) { 64 RawPtrMapTy Map = State->get<RawPtrMap>(); 65 for (const auto &Entry : Map) { 66 if (Entry.second.contains(Sym)) 67 return true; 68 } 69 return false; 70 } 71 }; 72 73 InnerPointerChecker() 74 : AppendFn({"std", "basic_string", "append"}), 75 AssignFn({"std", "basic_string", "assign"}), 76 AddressofFn({"std", "addressof"}), 77 ClearFn({"std", "basic_string", "clear"}), 78 CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1), 79 DataMemberFn({"std", "basic_string", "data"}), 80 EraseFn({"std", "basic_string", "erase"}), 81 InsertFn({"std", "basic_string", "insert"}), 82 PopBackFn({"std", "basic_string", "pop_back"}), 83 PushBackFn({"std", "basic_string", "push_back"}), 84 ReplaceFn({"std", "basic_string", "replace"}), 85 ReserveFn({"std", "basic_string", "reserve"}), 86 ResizeFn({"std", "basic_string", "resize"}), 87 ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), 88 SwapFn({"std", "basic_string", "swap"}) {} 89 90 /// Check whether the called member function potentially invalidates 91 /// pointers referring to the container object's inner buffer. 92 bool isInvalidatingMemberFunction(const CallEvent &Call) const; 93 94 /// Check whether the called function returns a raw inner pointer. 95 bool isInnerPointerAccessFunction(const CallEvent &Call) const; 96 97 /// Mark pointer symbols associated with the given memory region released 98 /// in the program state. 99 void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, 100 const MemRegion *ObjRegion, 101 CheckerContext &C) const; 102 103 /// Standard library functions that take a non-const `basic_string` argument by 104 /// reference may invalidate its inner pointers. Check for these cases and 105 /// mark the pointers released. 106 void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, 107 CheckerContext &C) const; 108 109 /// Record the connection between raw pointers referring to a container 110 /// object's inner buffer and the object's memory region in the program state. 111 /// Mark potentially invalidated pointers released. 112 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 113 114 /// Clean up the program state map. 115 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 116 }; 117 118 } // end anonymous namespace 119 120 bool InnerPointerChecker::isInvalidatingMemberFunction( 121 const CallEvent &Call) const { 122 if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) { 123 OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); 124 if (Opc == OO_Equal || Opc == OO_PlusEqual) 125 return true; 126 return false; 127 } 128 return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) || 129 Call.isCalled(AssignFn) || Call.isCalled(ClearFn) || 130 Call.isCalled(EraseFn) || Call.isCalled(InsertFn) || 131 Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) || 132 Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) || 133 Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) || 134 Call.isCalled(SwapFn)); 135 } 136 137 bool InnerPointerChecker::isInnerPointerAccessFunction( 138 const CallEvent &Call) const { 139 return (Call.isCalled(CStrFn) || Call.isCalled(DataFn) || 140 Call.isCalled(DataMemberFn)); 141 } 142 143 void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call, 144 ProgramStateRef State, 145 const MemRegion *MR, 146 CheckerContext &C) const { 147 if (const PtrSet *PS = State->get<RawPtrMap>(MR)) { 148 const Expr *Origin = Call.getOriginExpr(); 149 for (const auto Symbol : *PS) { 150 // NOTE: `Origin` may be null, and will be stored so in the symbol's 151 // `RefState` in MallocChecker's `RegionState` program state map. 152 State = allocation_state::markReleased(State, Symbol, Origin); 153 } 154 State = State->remove<RawPtrMap>(MR); 155 C.addTransition(State); 156 return; 157 } 158 } 159 160 void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call, 161 ProgramStateRef State, 162 CheckerContext &C) const { 163 if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) { 164 const FunctionDecl *FD = FC->getDecl(); 165 if (!FD || !FD->isInStdNamespace()) 166 return; 167 168 for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) { 169 QualType ParamTy = FD->getParamDecl(I)->getType(); 170 if (!ParamTy->isReferenceType() || 171 ParamTy->getPointeeType().isConstQualified()) 172 continue; 173 174 // In case of member operator calls, `this` is counted as an 175 // argument but not as a parameter. 176 bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC); 177 unsigned ArgI = isaMemberOpCall ? I+1 : I; 178 179 SVal Arg = FC->getArgSVal(ArgI); 180 const auto *ArgRegion = 181 dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion()); 182 if (!ArgRegion) 183 continue; 184 185 // std::addressof function accepts a non-const reference as an argument, 186 // but doesn't modify it. 187 if (Call.isCalled(AddressofFn)) 188 continue; 189 190 markPtrSymbolsReleased(Call, State, ArgRegion, C); 191 } 192 } 193 } 194 195 // [string.require] 196 // 197 // "References, pointers, and iterators referring to the elements of a 198 // basic_string sequence may be invalidated by the following uses of that 199 // basic_string object: 200 // 201 // -- As an argument to any standard library function taking a reference 202 // to non-const basic_string as an argument. For example, as an argument to 203 // non-member functions swap(), operator>>(), and getline(), or as an argument 204 // to basic_string::swap(). 205 // 206 // -- Calling non-const member functions, except operator[], at, front, back, 207 // begin, rbegin, end, and rend." 208 209 void InnerPointerChecker::checkPostCall(const CallEvent &Call, 210 CheckerContext &C) const { 211 ProgramStateRef State = C.getState(); 212 213 // TODO: Do we need these to be typed? 214 const TypedValueRegion *ObjRegion = nullptr; 215 216 if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) { 217 ObjRegion = dyn_cast_or_null<TypedValueRegion>( 218 ICall->getCXXThisVal().getAsRegion()); 219 220 // Check [string.require] / second point. 221 if (isInvalidatingMemberFunction(Call)) { 222 markPtrSymbolsReleased(Call, State, ObjRegion, C); 223 return; 224 } 225 } 226 227 if (isInnerPointerAccessFunction(Call)) { 228 229 if (isa<SimpleFunctionCall>(Call)) { 230 // NOTE: As of now, we only have one free access function: std::data. 231 // If we add more functions like this in the list, hardcoded 232 // argument index should be changed. 233 ObjRegion = 234 dyn_cast_or_null<TypedValueRegion>(Call.getArgSVal(0).getAsRegion()); 235 } 236 237 if (!ObjRegion) 238 return; 239 240 SVal RawPtr = Call.getReturnValue(); 241 if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { 242 // Start tracking this raw pointer by adding it to the set of symbols 243 // associated with this container object in the program state map. 244 245 PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); 246 const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion); 247 PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); 248 assert(C.wasInlined || !Set.contains(Sym)); 249 Set = F.add(Set, Sym); 250 251 State = State->set<RawPtrMap>(ObjRegion, Set); 252 C.addTransition(State); 253 } 254 255 return; 256 } 257 258 // Check [string.require] / first point. 259 checkFunctionArguments(Call, State, C); 260 } 261 262 void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, 263 CheckerContext &C) const { 264 ProgramStateRef State = C.getState(); 265 PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); 266 RawPtrMapTy RPM = State->get<RawPtrMap>(); 267 for (const auto &Entry : RPM) { 268 if (!SymReaper.isLiveRegion(Entry.first)) { 269 // Due to incomplete destructor support, some dead regions might 270 // remain in the program state map. Clean them up. 271 State = State->remove<RawPtrMap>(Entry.first); 272 } 273 if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) { 274 PtrSet CleanedUpSet = *OldSet; 275 for (const auto Symbol : Entry.second) { 276 if (!SymReaper.isLive(Symbol)) 277 CleanedUpSet = F.remove(CleanedUpSet, Symbol); 278 } 279 State = CleanedUpSet.isEmpty() 280 ? State->remove<RawPtrMap>(Entry.first) 281 : State->set<RawPtrMap>(Entry.first, CleanedUpSet); 282 } 283 } 284 C.addTransition(State); 285 } 286 287 namespace clang { 288 namespace ento { 289 namespace allocation_state { 290 291 std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) { 292 return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym); 293 } 294 295 const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { 296 RawPtrMapTy Map = State->get<RawPtrMap>(); 297 for (const auto &Entry : Map) { 298 if (Entry.second.contains(Sym)) { 299 return Entry.first; 300 } 301 } 302 return nullptr; 303 } 304 305 } // end namespace allocation_state 306 } // end namespace ento 307 } // end namespace clang 308 309 PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode( 310 const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { 311 if (!isSymbolTracked(N->getState(), PtrToBuf) || 312 isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)) 313 return nullptr; 314 315 const Stmt *S = N->getStmtForDiagnostics(); 316 if (!S) 317 return nullptr; 318 319 const MemRegion *ObjRegion = 320 allocation_state::getContainerObjRegion(N->getState(), PtrToBuf); 321 const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); 322 QualType ObjTy = TypedRegion->getValueType(); 323 324 SmallString<256> Buf; 325 llvm::raw_svector_ostream OS(Buf); 326 OS << "Pointer to inner buffer of '" << ObjTy.getAsString() 327 << "' obtained here"; 328 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 329 N->getLocationContext()); 330 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 331 } 332 333 void ento::registerInnerPointerChecker(CheckerManager &Mgr) { 334 registerInnerPointerCheckerAux(Mgr); 335 Mgr.registerChecker<InnerPointerChecker>(); 336 } 337 338 bool ento::shouldRegisterInnerPointerChecker(const CheckerManager &mgr) { 339 return true; 340 } 341