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