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