1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
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 | |
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, ClearFn, CStrFn, DataFn, EraseFn, |
38 | InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, |
39 | 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 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, |
58 | BugReporterContext &BRC, |
59 | BugReport &BR) override; |
60 | |
61 | |
62 | |
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 | ClearFn({"std", "basic_string", "clear"}), |
77 | CStrFn({"std", "basic_string", "c_str"}), |
78 | DataFn({"std", "basic_string", "data"}), |
79 | EraseFn({"std", "basic_string", "erase"}), |
80 | InsertFn({"std", "basic_string", "insert"}), |
81 | PopBackFn({"std", "basic_string", "pop_back"}), |
82 | PushBackFn({"std", "basic_string", "push_back"}), |
83 | ReplaceFn({"std", "basic_string", "replace"}), |
84 | ReserveFn({"std", "basic_string", "reserve"}), |
85 | ResizeFn({"std", "basic_string", "resize"}), |
86 | ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), |
87 | SwapFn({"std", "basic_string", "swap"}) {} |
88 | |
89 | |
90 | |
91 | bool isInvalidatingMemberFunction(const CallEvent &Call) const; |
92 | |
93 | |
94 | |
95 | void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, |
96 | const MemRegion *ObjRegion, |
97 | CheckerContext &C) const; |
98 | |
99 | |
100 | |
101 | |
102 | void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, |
103 | CheckerContext &C) const; |
104 | |
105 | |
106 | |
107 | |
108 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
109 | |
110 | |
111 | void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; |
112 | }; |
113 | |
114 | } |
115 | |
116 | bool InnerPointerChecker::isInvalidatingMemberFunction( |
117 | const CallEvent &Call) const { |
118 | if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) { |
119 | OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); |
120 | if (Opc == OO_Equal || Opc == OO_PlusEqual) |
121 | return true; |
122 | return false; |
123 | } |
124 | return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) || |
125 | Call.isCalled(AssignFn) || Call.isCalled(ClearFn) || |
126 | Call.isCalled(EraseFn) || Call.isCalled(InsertFn) || |
127 | Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) || |
128 | Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) || |
129 | Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) || |
130 | Call.isCalled(SwapFn)); |
131 | } |
132 | |
133 | void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call, |
134 | ProgramStateRef State, |
135 | const MemRegion *MR, |
136 | CheckerContext &C) const { |
137 | if (const PtrSet *PS = State->get<RawPtrMap>(MR)) { |
138 | const Expr *Origin = Call.getOriginExpr(); |
139 | for (const auto Symbol : *PS) { |
140 | |
141 | |
142 | State = allocation_state::markReleased(State, Symbol, Origin); |
143 | } |
144 | State = State->remove<RawPtrMap>(MR); |
145 | C.addTransition(State); |
146 | return; |
147 | } |
148 | } |
149 | |
150 | void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call, |
151 | ProgramStateRef State, |
152 | CheckerContext &C) const { |
153 | if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) { |
154 | const FunctionDecl *FD = FC->getDecl(); |
155 | if (!FD || !FD->isInStdNamespace()) |
156 | return; |
157 | |
158 | for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) { |
159 | QualType ParamTy = FD->getParamDecl(I)->getType(); |
160 | if (!ParamTy->isReferenceType() || |
161 | ParamTy->getPointeeType().isConstQualified()) |
162 | continue; |
163 | |
164 | |
165 | |
166 | bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC); |
167 | unsigned ArgI = isaMemberOpCall ? I+1 : I; |
168 | |
169 | SVal Arg = FC->getArgSVal(ArgI); |
170 | const auto *ArgRegion = |
171 | dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion()); |
172 | if (!ArgRegion) |
173 | continue; |
174 | |
175 | markPtrSymbolsReleased(Call, State, ArgRegion, C); |
176 | } |
177 | } |
178 | } |
179 | |
180 | |
181 | |
182 | |
183 | |
184 | |
185 | |
186 | |
187 | |
188 | |
189 | |
190 | |
191 | |
192 | |
193 | |
194 | void InnerPointerChecker::checkPostCall(const CallEvent &Call, |
195 | CheckerContext &C) const { |
196 | ProgramStateRef State = C.getState(); |
197 | |
198 | if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) { |
199 | |
200 | const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>( |
201 | ICall->getCXXThisVal().getAsRegion()); |
202 | if (!ObjRegion) |
203 | return; |
204 | |
205 | if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { |
206 | SVal RawPtr = Call.getReturnValue(); |
207 | if (SymbolRef Sym = RawPtr.getAsSymbol()) { |
208 | |
209 | |
210 | |
211 | PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); |
212 | const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion); |
213 | PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); |
214 | assert(C.wasInlined || !Set.contains(Sym)); |
215 | Set = F.add(Set, Sym); |
216 | |
217 | State = State->set<RawPtrMap>(ObjRegion, Set); |
218 | C.addTransition(State); |
219 | } |
220 | return; |
221 | } |
222 | |
223 | |
224 | if (isInvalidatingMemberFunction(Call)) { |
225 | markPtrSymbolsReleased(Call, State, ObjRegion, C); |
226 | return; |
227 | } |
228 | } |
229 | |
230 | |
231 | checkFunctionArguments(Call, State, C); |
232 | } |
233 | |
234 | void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, |
235 | CheckerContext &C) const { |
236 | ProgramStateRef State = C.getState(); |
237 | PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); |
238 | RawPtrMapTy RPM = State->get<RawPtrMap>(); |
239 | for (const auto Entry : RPM) { |
240 | if (!SymReaper.isLiveRegion(Entry.first)) { |
241 | |
242 | |
243 | State = State->remove<RawPtrMap>(Entry.first); |
244 | } |
245 | if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) { |
246 | PtrSet CleanedUpSet = *OldSet; |
247 | for (const auto Symbol : Entry.second) { |
248 | if (!SymReaper.isLive(Symbol)) |
249 | CleanedUpSet = F.remove(CleanedUpSet, Symbol); |
250 | } |
251 | State = CleanedUpSet.isEmpty() |
252 | ? State->remove<RawPtrMap>(Entry.first) |
253 | : State->set<RawPtrMap>(Entry.first, CleanedUpSet); |
254 | } |
255 | } |
256 | C.addTransition(State); |
257 | } |
258 | |
259 | namespace clang { |
260 | namespace ento { |
261 | namespace allocation_state { |
262 | |
263 | std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) { |
264 | return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym); |
265 | } |
266 | |
267 | const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { |
268 | RawPtrMapTy Map = State->get<RawPtrMap>(); |
269 | for (const auto Entry : Map) { |
270 | if (Entry.second.contains(Sym)) { |
271 | return Entry.first; |
272 | } |
273 | } |
274 | return nullptr; |
275 | } |
276 | |
277 | } |
278 | } |
279 | } |
280 | |
281 | std::shared_ptr<PathDiagnosticPiece> |
282 | InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, |
283 | BugReporterContext &BRC, |
284 | BugReport &) { |
285 | if (!isSymbolTracked(N->getState(), PtrToBuf) || |
286 | isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)) |
287 | return nullptr; |
288 | |
289 | const Stmt *S = PathDiagnosticLocation::getStmt(N); |
290 | if (!S) |
291 | return nullptr; |
292 | |
293 | const MemRegion *ObjRegion = |
294 | allocation_state::getContainerObjRegion(N->getState(), PtrToBuf); |
295 | const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); |
296 | QualType ObjTy = TypedRegion->getValueType(); |
297 | |
298 | SmallString<256> Buf; |
299 | llvm::raw_svector_ostream OS(Buf); |
300 | OS << "Pointer to inner buffer of '" << ObjTy.getAsString() |
301 | << "' obtained here"; |
302 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
303 | N->getLocationContext()); |
304 | return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, |
305 | nullptr); |
306 | } |
307 | |
308 | void ento::registerInnerPointerChecker(CheckerManager &Mgr) { |
309 | registerInnerPointerCheckerAux(Mgr); |
310 | Mgr.registerChecker<InnerPointerChecker>(); |
311 | } |
312 | |
313 | bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) { |
314 | return true; |
315 | } |
316 | |