clang 23.0.0git
SmartPtrModeling.cpp
Go to the documentation of this file.
1// SmartPtrModeling.cpp - Model behavior of C++ smart pointers - 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 checker that models various aspects of
10// C++ smart pointer behavior.
11//
12//===----------------------------------------------------------------------===//
13
14#include "Move.h"
15#include "SmartPtr.h"
16
17#include "clang/AST/DeclCXX.h"
19#include "clang/AST/ExprCXX.h"
20#include "clang/AST/Type.h"
21#include "clang/Basic/LLVM.h"
33#include "llvm/ADT/STLExtras.h"
34#include "llvm/Support/ErrorHandling.h"
35#include <optional>
36
37using namespace clang;
38using namespace ento;
39
40namespace {
41
42class SmartPtrModeling
43 : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
44 check::LiveSymbols> {
45
46 bool isBoolConversionMethod(const CallEvent &Call) const;
47
48public:
49 // Whether the checker should model for null dereferences of smart pointers.
50 bool ModelSmartPtrDereference = false;
51 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
52 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
54 checkRegionChanges(ProgramStateRef State,
55 const InvalidatedSymbols *Invalidated,
56 ArrayRef<const MemRegion *> ExplicitRegions,
57 ArrayRef<const MemRegion *> Regions, const StackFrame *SF,
58 const CallEvent *Call) const;
59 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
60 const char *Sep) const override;
61 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
62
63private:
64 void handleReset(const CallEvent &Call, CheckerContext &C) const;
65 void handleRelease(const CallEvent &Call, CheckerContext &C) const;
66 void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const;
67 void handleGet(const CallEvent &Call, CheckerContext &C) const;
68 bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
69 bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
70 const MemRegion *ThisRegion) const;
71 bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
72 const MemRegion *OtherSmartPtrRegion,
73 const CallEvent &Call) const;
74 void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
75 bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
76 bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
77 bool handleSwap(ProgramStateRef State, SVal First, SVal Second,
78 CheckerContext &C) const;
79 std::pair<SVal, ProgramStateRef> retrieveOrConjureInnerPtrVal(
80 ProgramStateRef State, const MemRegion *ThisRegion,
81 ConstCFGElementRef Elem, QualType Type, CheckerContext &C) const;
82
83 using SmartPtrMethodHandlerFn =
84 void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
85 CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
86 {{CDM::CXXMethod, {"reset"}}, &SmartPtrModeling::handleReset},
87 {{CDM::CXXMethod, {"release"}}, &SmartPtrModeling::handleRelease},
88 {{CDM::CXXMethod, {"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
89 {{CDM::CXXMethod, {"get"}}, &SmartPtrModeling::handleGet}};
90 const CallDescription StdSwapCall{CDM::SimpleFunc, {"std", "swap"}, 2};
91 const CallDescriptionSet MakeUniqueVariants{
92 {CDM::SimpleFunc, {"std", "make_unique"}},
93 {CDM::SimpleFunc, {"std", "make_unique_for_overwrite"}}};
94};
95} // end of anonymous namespace
96
97REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
98
99// Checks if RD has name in Names and is in std namespace
100static bool hasStdClassWithName(const CXXRecordDecl *RD,
101 ArrayRef<llvm::StringLiteral> Names) {
102 if (!RD || !RD->getDeclContext()->isStdNamespace())
103 return false;
104 if (RD->getDeclName().isIdentifier())
105 return llvm::is_contained(Names, RD->getName());
106 return false;
107}
108
109constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
110 "weak_ptr"};
111
112static bool isStdSmartPtr(const CXXRecordDecl *RD) {
114}
115
116static bool isStdSmartPtr(const Expr *E) {
118}
119
120// Define the inter-checker API.
121namespace clang {
122namespace ento {
123namespace smartptr {
125 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
126 if (!MethodDecl || !MethodDecl->getParent())
127 return false;
128 return isStdSmartPtr(MethodDecl->getParent());
129}
130
132 if (!RD || !RD->getDeclContext()->isStdNamespace())
133 return false;
134
135 if (RD->getDeclName().isIdentifier()) {
136 StringRef Name = RD->getName();
137 return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
138 }
139 return false;
140}
141
142bool isStdSmartPtr(const Expr *E) {
144}
145
146bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
147 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
148 return InnerPointVal &&
149 !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
150}
151} // namespace smartptr
152} // namespace ento
153} // namespace clang
154
155// If a region is removed all of the subregions need to be removed too.
156static TrackedRegionMapTy
157removeTrackedSubregions(TrackedRegionMapTy RegionMap,
158 TrackedRegionMapTy::Factory &RegionMapFactory,
159 const MemRegion *Region) {
160 if (!Region)
161 return RegionMap;
162 for (const auto &E : RegionMap) {
163 if (E.first->isSubRegionOf(Region))
164 RegionMap = RegionMapFactory.remove(RegionMap, E.first);
165 }
166 return RegionMap;
167}
168
170 const MemRegion *Region,
171 const SVal *RegionInnerPointerVal) {
172 if (RegionInnerPointerVal) {
173 State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
174 } else {
175 State = State->remove<TrackedRegionMap>(Region);
176 }
177 return State;
178}
179
181 if (!RD || !RD->isInStdNamespace())
182 return {};
183
184 const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
185 if (!TSD)
186 return {};
187
188 auto TemplateArgs = TSD->getTemplateArgs().asArray();
189 if (TemplateArgs.empty())
190 return {};
191 auto InnerValueType = TemplateArgs[0].getAsType();
192 return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
193}
194
195// This is for use with standalone-functions like std::make_unique,
196// std::make_unique_for_overwrite, etc. It reads the template parameter and
197// returns the pointer type corresponding to it,
199 CheckerContext &C) {
200 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
201 if (!FD || !FD->getPrimaryTemplate())
202 return {};
203 const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
204 if (TemplateArgs.size() == 0)
205 return {};
206 auto ValueType = TemplateArgs[0].getAsType();
207 return C.getASTContext().getPointerType(ValueType.getCanonicalType());
208}
209
210// Helper method to get the inner pointer type of specialized smart pointer
211// Returns empty type if not found valid inner pointer type.
213 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
214 if (!MethodDecl || !MethodDecl->getParent())
215 return {};
216
217 const auto *RecordDecl = MethodDecl->getParent();
219}
220
221// Helper method to pretty print region and avoid extra spacing.
222static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
223 const MemRegion *Region) {
224 if (Region->canPrintPretty()) {
225 OS << " ";
226 Region->printPretty(OS);
227 }
228}
229
230bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
231 // TODO: Update CallDescription to support anonymous calls?
232 // TODO: Handle other methods, such as .get() or .release().
233 // But once we do, we'd need a visitor to explain null dereferences
234 // that are found via such modeling.
235 const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
236 return CD && CD->getConversionType()->isBooleanType();
237}
238
239constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
240
241static bool isStdBasicOstream(const Expr *E) {
242 const auto *RD = E->getType()->getAsCXXRecordDecl();
244}
245
246static bool isStdFunctionCall(const CallEvent &Call) {
247 return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
248}
249
251 if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
252 return false;
253 const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
254 if (!FC)
255 return false;
256 const FunctionDecl *FD = FC->getDecl();
257 if (!FD->isOverloadedOperator())
258 return false;
260 if (OOK != clang::OO_LessLess)
261 return false;
262 return isStdSmartPtr(Call.getArgExpr(1)) &&
263 isStdBasicOstream(Call.getArgExpr(0));
264}
265
267 if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
268 return false;
269 return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
270 smartptr::isStdSmartPtr(Call.getArgExpr(1));
271}
272
273bool SmartPtrModeling::evalCall(const CallEvent &Call,
274 CheckerContext &C) const {
275
276 ProgramStateRef State = C.getState();
277
278 // If any one of the arg is a unique_ptr, then
279 // we can try this function
280 if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
281 if (handleComparisionOp(Call, C))
282 return true;
283
284 if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
285 return handleOstreamOperator(Call, C);
286
287 if (StdSwapCall.matches(Call)) {
288 // Check the first arg, if it is of std::unique_ptr type.
289 assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
290 const Expr *FirstArg = Call.getArgExpr(0);
292 return false;
293 return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
294 }
295
296 if (MakeUniqueVariants.contains(Call)) {
297 if (!ModelSmartPtrDereference)
298 return false;
299
300 const std::optional<SVal> ThisRegionOpt =
301 Call.getReturnValueUnderConstruction();
302 if (!ThisRegionOpt)
303 return false;
304
305 const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal(
306 Call.getCFGElementRef(), C.getStackFrame(),
307 getPointerTypeFromTemplateArg(Call, C), C.blockCount());
308
309 const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
310 State = State->set<TrackedRegionMap>(ThisRegion, PtrVal);
311 State = State->assume(PtrVal, true);
312
313 // TODO: ExprEngine should do this for us.
314 // For a bit more context:
315 // 1) Why do we need this? Since we are modelling a "function"
316 // that returns a constructed object we need to store this information in
317 // the program state.
318 //
319 // 2) Why does this work?
320 // `updateObjectsUnderConstruction` does exactly as it sounds.
321 //
322 // 3) How should it look like when moved to the Engine?
323 // It would be nice if we can just
324 // pretend we don't need to know about this - ie, completely automatic work.
325 // However, realistically speaking, I think we would need to "signal" the
326 // ExprEngine evalCall handler that we are constructing an object with this
327 // function call (constructors obviously construct, hence can be
328 // automatically deduced).
329 auto &Engine = State->getStateManager().getOwningEngine();
330 State = Engine.updateObjectsUnderConstruction(
331 *ThisRegionOpt, nullptr, State, C.getStackFrame(),
332 Call.getConstructionContext(), {});
333
334 // We don't leave a note here since it is guaranteed the
335 // unique_ptr from this call is non-null (hence is safe to de-reference).
336 C.addTransition(State);
337 return true;
338 }
339
341 return false;
342
343 if (isBoolConversionMethod(Call)) {
344 const MemRegion *ThisR =
345 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
346
347 if (ModelSmartPtrDereference) {
348 // The check for the region is moved is duplicated in handleBoolOperation
349 // method.
350 // FIXME: Once we model std::move for smart pointers clean up this and use
351 // that modeling.
352 handleBoolConversion(Call, C);
353 return true;
354 } else {
355 if (!move::isMovedFrom(State, ThisR)) {
356 // TODO: Model this case as well. At least, avoid invalidation of
357 // globals.
358 return false;
359 }
360
361 // TODO: Add a note to bug reports describing this decision.
362 C.addTransition(State->BindExpr(
363 Call.getOriginExpr(), C.getStackFrame(),
364 C.getSValBuilder().makeZeroVal(Call.getResultType())));
365
366 return true;
367 }
368 }
369
370 if (!ModelSmartPtrDereference)
371 return false;
372
373 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
374 if (CC->getDecl()->isCopyConstructor())
375 return false;
376
377 const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
378 if (!ThisRegion)
379 return false;
380
381 QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
382
383 if (CC->getDecl()->isMoveConstructor())
384 return handleMoveCtr(Call, C, ThisRegion);
385
386 if (Call.getNumArgs() == 0) {
387 auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
388 State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
389
390 C.addTransition(
391 State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
392 llvm::raw_ostream &OS) {
393 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
394 !BR.isInteresting(ThisRegion))
395 return;
396 OS << "Default constructed smart pointer";
397 checkAndPrettyPrintRegion(OS, ThisRegion);
398 OS << " is null";
399 }));
400 } else {
401 const auto *TrackingExpr = Call.getArgExpr(0);
402 assert(TrackingExpr->getType()->isPointerType() &&
403 "Adding a non pointer value to TrackedRegionMap");
404 auto ArgVal = Call.getArgSVal(0);
405 State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
406
407 C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
408 ArgVal](PathSensitiveBugReport &BR,
409 llvm::raw_ostream &OS) {
410 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
411 !BR.isInteresting(ThisRegion))
412 return;
413 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
414 OS << "Smart pointer";
415 checkAndPrettyPrintRegion(OS, ThisRegion);
416 if (ArgVal.isZeroConstant())
417 OS << " is constructed using a null value";
418 else
419 OS << " is constructed";
420 }));
421 }
422 return true;
423 }
424
425 if (handleAssignOp(Call, C))
426 return true;
427
428 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
429 if (!Handler)
430 return false;
431 (this->**Handler)(Call, C);
432
433 return C.isDifferent();
434}
435
436std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
437 ProgramStateRef State, const MemRegion *ThisRegion, ConstCFGElementRef Elem,
438 QualType Type, CheckerContext &C) const {
439 const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
440 if (Ptr)
441 return {*Ptr, State};
442 auto Val = C.getSValBuilder().conjureSymbolVal(Elem, C.getStackFrame(), Type,
443 C.blockCount());
444 State = State->set<TrackedRegionMap>(ThisRegion, Val);
445 return {Val, State};
446}
447
448bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
449 CheckerContext &C) const {
450 const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
451 if (!FC)
452 return false;
453 const FunctionDecl *FD = FC->getDecl();
454 if (!FD->isOverloadedOperator())
455 return false;
457 if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
458 OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
459 OOK == OO_Spaceship))
460 return false;
461
462 // There are some special cases about which we can infer about
463 // the resulting answer.
464 // For reference, there is a discussion at https://reviews.llvm.org/D104616.
465 // Also, the cppreference page is good to look at
466 // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
467
468 auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
470 SVal S) -> std::pair<SVal, ProgramStateRef> {
471 if (S.isZeroConstant()) {
472 return {S, State};
473 }
474 const MemRegion *Reg = S.getAsRegion();
475 assert(Reg &&
476 "this pointer of std::unique_ptr should be obtainable as MemRegion");
477 QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
478 return retrieveOrConjureInnerPtrVal(State, Reg, Elem, Type, C);
479 };
480
481 SVal First = Call.getArgSVal(0);
482 SVal Second = Call.getArgSVal(1);
483 const auto *FirstExpr = Call.getArgExpr(0);
484 const auto *SecondExpr = Call.getArgExpr(1);
485
486 const auto *ResultExpr = Call.getOriginExpr();
487 const auto *SF = C.getStackFrame();
488 auto &Bldr = C.getSValBuilder();
489 ProgramStateRef State = C.getState();
490
491 SVal FirstPtrVal, SecondPtrVal;
492 std::tie(FirstPtrVal, State) =
493 makeSValFor(State, FirstExpr, Call.getCFGElementRef(), First);
494 std::tie(SecondPtrVal, State) =
495 makeSValFor(State, SecondExpr, Call.getCFGElementRef(), Second);
498 auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
499 Call.getResultType());
500
501 if (OOK != OO_Spaceship) {
502 ProgramStateRef TrueState, FalseState;
503 std::tie(TrueState, FalseState) =
504 State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
505 if (TrueState)
506 C.addTransition(
507 TrueState->BindExpr(ResultExpr, SF, Bldr.makeTruthVal(true)));
508 if (FalseState)
509 C.addTransition(
510 FalseState->BindExpr(ResultExpr, SF, Bldr.makeTruthVal(false)));
511 } else {
512 C.addTransition(State->BindExpr(ResultExpr, SF, RetVal));
513 }
514 return true;
515}
516
517bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
518 CheckerContext &C) const {
519 // operator<< does not modify the smart pointer.
520 // And we don't really have much of modelling of basic_ostream.
521 // So, we are better off:
522 // 1) Invalidating the mem-region of the ostream object at hand.
523 // 2) Setting the SVal of the basic_ostream as the return value.
524 // Not very satisfying, but it gets the job done, and is better
525 // than the default handling. :)
526
527 ProgramStateRef State = C.getState();
528 const auto StreamVal = Call.getArgSVal(0);
529 const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
530 if (!StreamThisRegion)
531 return false;
532 State = State->invalidateRegions({StreamThisRegion}, Call.getCFGElementRef(),
533 C.blockCount(), C.getStackFrame(), false);
534 State = State->BindExpr(Call.getOriginExpr(), C.getStackFrame(), StreamVal);
535 C.addTransition(State);
536 return true;
537}
538
539void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
540 CheckerContext &C) const {
541 ProgramStateRef State = C.getState();
542 // Clean up dead regions from the region map.
543 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
544 for (auto E : TrackedRegions) {
545 const MemRegion *Region = E.first;
546 bool IsRegDead = !SymReaper.isLiveRegion(Region);
547
548 if (IsRegDead)
549 State = State->remove<TrackedRegionMap>(Region);
550 }
551 C.addTransition(State);
552}
553
554void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
555 const char *NL, const char *Sep) const {
556 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
557
558 if (!RS.isEmpty()) {
559 Out << Sep << "Smart ptr regions :" << NL;
560 for (auto I : RS) {
561 I.first->dumpToStream(Out);
562 if (smartptr::isNullSmartPtr(State, I.first))
563 Out << ": Null";
564 else
565 Out << ": Non Null";
566 Out << NL;
567 }
568 }
569}
570
571ProgramStateRef SmartPtrModeling::checkRegionChanges(
572 ProgramStateRef State, const InvalidatedSymbols *Invalidated,
573 ArrayRef<const MemRegion *> ExplicitRegions,
574 ArrayRef<const MemRegion *> Regions, const StackFrame *SF,
575 const CallEvent *Call) const {
576 TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
577 TrackedRegionMapTy::Factory &RegionMapFactory =
578 State->get_context<TrackedRegionMap>();
579 for (const auto *Region : Regions)
580 RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
581 Region->getBaseRegion());
582 return State->set<TrackedRegionMap>(RegionMap);
583}
584
585void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
586 SymbolReaper &SR) const {
587 // Marking tracked symbols alive
588 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
589 for (SVal Val : llvm::make_second_range(TrackedRegions)) {
590 for (SymbolRef Sym : Val.symbols()) {
591 SR.markLive(Sym);
592 }
593 }
594}
595
596void SmartPtrModeling::handleReset(const CallEvent &Call,
597 CheckerContext &C) const {
598 ProgramStateRef State = C.getState();
599 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
600 if (!IC)
601 return;
602
603 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
604 if (!ThisRegion)
605 return;
606
607 assert(Call.getArgExpr(0)->getType()->isPointerType() &&
608 "Adding a non pointer value to TrackedRegionMap");
609 State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
610 const auto *TrackingExpr = Call.getArgExpr(0);
611 C.addTransition(
612 State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
613 llvm::raw_ostream &OS) {
614 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
615 !BR.isInteresting(ThisRegion))
616 return;
617 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
618 OS << "Smart pointer";
619 checkAndPrettyPrintRegion(OS, ThisRegion);
620 OS << " reset using a null value";
621 }));
622 // TODO: Make sure to ivalidate the region in the Store if we don't have
623 // time to model all methods.
624}
625
626void SmartPtrModeling::handleRelease(const CallEvent &Call,
627 CheckerContext &C) const {
628 ProgramStateRef State = C.getState();
629 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
630 if (!IC)
631 return;
632
633 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
634 if (!ThisRegion)
635 return;
636
637 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
638
639 if (InnerPointVal) {
640 State = State->BindExpr(Call.getOriginExpr(), C.getStackFrame(),
641 *InnerPointVal);
642 }
643
644 QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
645 auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType);
646 State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
647
648 C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
649 llvm::raw_ostream &OS) {
650 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
651 !BR.isInteresting(ThisRegion))
652 return;
653
654 OS << "Smart pointer";
655 checkAndPrettyPrintRegion(OS, ThisRegion);
656 OS << " is released and set to null";
657 }));
658 // TODO: Add support to enable MallocChecker to start tracking the raw
659 // pointer.
660}
661
662void SmartPtrModeling::handleSwapMethod(const CallEvent &Call,
663 CheckerContext &C) const {
664 // To model unique_ptr::swap() method.
665 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
666 if (!IC)
667 return;
668
669 auto State = C.getState();
670 handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
671}
672
673bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
674 SVal Second, CheckerContext &C) const {
675 const MemRegion *FirstThisRegion = First.getAsRegion();
676 if (!FirstThisRegion)
677 return false;
678 const MemRegion *SecondThisRegion = Second.getAsRegion();
679 if (!SecondThisRegion)
680 return false;
681
682 const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
683 const auto *SecondInnerPtrVal =
684 State->get<TrackedRegionMap>(SecondThisRegion);
685
686 State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
687 State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
688
689 C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
690 PathSensitiveBugReport &BR,
691 llvm::raw_ostream &OS) {
692 if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
693 return;
694 if (BR.isInteresting(FirstThisRegion) &&
695 !BR.isInteresting(SecondThisRegion)) {
696 BR.markInteresting(SecondThisRegion);
697 BR.markNotInteresting(FirstThisRegion);
698 }
699 if (BR.isInteresting(SecondThisRegion) &&
700 !BR.isInteresting(FirstThisRegion)) {
701 BR.markInteresting(FirstThisRegion);
702 BR.markNotInteresting(SecondThisRegion);
703 }
704 // TODO: We need to emit some note here probably!!
705 }));
706
707 return true;
708}
709
710void SmartPtrModeling::handleGet(const CallEvent &Call,
711 CheckerContext &C) const {
712 ProgramStateRef State = C.getState();
713 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
714 if (!IC)
715 return;
716
717 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
718 if (!ThisRegion)
719 return;
720
721 SVal InnerPointerVal;
722 std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
723 State, ThisRegion, Call.getCFGElementRef(), Call.getResultType(), C);
724 State =
725 State->BindExpr(Call.getOriginExpr(), C.getStackFrame(), InnerPointerVal);
726 // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
727 C.addTransition(State);
728}
729
730bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
731 CheckerContext &C) const {
732 ProgramStateRef State = C.getState();
733 const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
734 if (!OC)
735 return false;
736 OverloadedOperatorKind OOK = OC->getOverloadedOperator();
737 if (OOK != OO_Equal)
738 return false;
739 const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
740 if (!ThisRegion)
741 return false;
742
743 QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
744
745 const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
746 // In case of 'nullptr' or '0' assigned
747 if (!OtherSmartPtrRegion) {
748 bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
749 if (!AssignedNull)
750 return false;
751 auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
752 State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
753 C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
754 llvm::raw_ostream &OS) {
755 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
756 !BR.isInteresting(ThisRegion))
757 return;
758 OS << "Smart pointer";
759 checkAndPrettyPrintRegion(OS, ThisRegion);
760 OS << " is assigned to null";
761 }));
762 return true;
763 }
764
765 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
766}
767
768bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
769 const MemRegion *ThisRegion) const {
770 const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
771 if (!OtherSmartPtrRegion)
772 return false;
773
774 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
775}
776
777bool SmartPtrModeling::updateMovedSmartPointers(
778 CheckerContext &C, const MemRegion *ThisRegion,
779 const MemRegion *OtherSmartPtrRegion, const CallEvent &Call) const {
780 ProgramStateRef State = C.getState();
781 QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
782 const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
783 if (OtherInnerPtr) {
784 State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
785
786 auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
787 State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
788 bool IsArgValNull = OtherInnerPtr->isZeroConstant();
789
790 C.addTransition(
791 State,
792 C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
793 PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
794 if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
795 return;
796 if (BR.isInteresting(OtherSmartPtrRegion)) {
797 OS << "Smart pointer";
798 checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
799 OS << " is null after being moved to";
800 checkAndPrettyPrintRegion(OS, ThisRegion);
801 }
802 if (BR.isInteresting(ThisRegion) && IsArgValNull) {
803 OS << "A null pointer value is moved to";
804 checkAndPrettyPrintRegion(OS, ThisRegion);
805 BR.markInteresting(OtherSmartPtrRegion);
806 }
807 }));
808 return true;
809 } else {
810 // In case we dont know anything about value we are moving from
811 // remove the entry from map for which smart pointer got moved to.
812 // For unique_ptr<A>, Ty will be 'A*'.
813 auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
814 State = State->remove<TrackedRegionMap>(ThisRegion);
815 State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
816 C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
817 ThisRegion](PathSensitiveBugReport &BR,
818 llvm::raw_ostream &OS) {
819 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
820 !BR.isInteresting(OtherSmartPtrRegion))
821 return;
822 OS << "Smart pointer";
823 checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
824 OS << " is null after; previous value moved to";
825 checkAndPrettyPrintRegion(OS, ThisRegion);
826 }));
827 return true;
828 }
829 return false;
830}
831
832void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
833 CheckerContext &C) const {
834 // To model unique_ptr::operator bool
835 ProgramStateRef State = C.getState();
836 const Expr *CallExpr = Call.getOriginExpr();
837 const MemRegion *ThisRegion =
838 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
839
840 QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
841
842 SVal InnerPointerVal;
843 if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
844 InnerPointerVal = *InnerValPtr;
845 } else {
846 // In case of inner pointer SVal is not available we create
847 // conjureSymbolVal for inner pointer value.
848 auto InnerPointerType = getInnerPointerType(Call, C);
849 if (InnerPointerType.isNull())
850 return;
851
852 InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
853 Call, InnerPointerType, C.blockCount());
854 State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
855 }
856
857 if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
858 State = State->BindExpr(CallExpr, C.getStackFrame(),
859 C.getSValBuilder().makeTruthVal(false));
860
861 C.addTransition(State);
862 return;
863 } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
864 State = State->BindExpr(CallExpr, C.getStackFrame(),
865 C.getSValBuilder().makeTruthVal(true));
866
867 C.addTransition(State);
868 return;
869 } else if (move::isMovedFrom(State, ThisRegion)) {
870 C.addTransition(
871 State->BindExpr(CallExpr, C.getStackFrame(),
872 C.getSValBuilder().makeZeroVal(Call.getResultType())));
873 return;
874 } else {
875 ProgramStateRef NotNullState, NullState;
876 std::tie(NotNullState, NullState) =
877 State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
878
879 auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
880 // Explicitly tracking the region as null.
881 NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
882
883 NullState = NullState->BindExpr(CallExpr, C.getStackFrame(),
884 C.getSValBuilder().makeTruthVal(false));
885 C.addTransition(NullState, C.getNoteTag(
886 [ThisRegion](PathSensitiveBugReport &BR,
887 llvm::raw_ostream &OS) {
888 OS << "Assuming smart pointer";
889 checkAndPrettyPrintRegion(OS, ThisRegion);
890 OS << " is null";
891 },
892 /*IsPrunable=*/true));
893 NotNullState = NotNullState->BindExpr(
894 CallExpr, C.getStackFrame(), C.getSValBuilder().makeTruthVal(true));
895 C.addTransition(
896 NotNullState,
897 C.getNoteTag(
898 [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
899 OS << "Assuming smart pointer";
900 checkAndPrettyPrintRegion(OS, ThisRegion);
901 OS << " is non-null";
902 },
903 /*IsPrunable=*/true));
904 return;
905 }
906}
907
908void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
909 auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
910 Checker->ModelSmartPtrDereference =
911 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
912 Checker, "ModelSmartPtrDereference");
913}
914
915bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
916 const LangOptions &LO = mgr.getLangOpts();
917 return LO.CPlusPlus;
918}
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static bool isStdFunctionCall(const CallEvent &Call)
static bool isStdBasicOstream(const Expr *E)
static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, const MemRegion *Region)
static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD)
static ProgramStateRef updateSwappedRegion(ProgramStateRef State, const MemRegion *Region, const SVal *RegionInnerPointerVal)
static bool isStdOstreamOperatorCall(const CallEvent &Call)
static TrackedRegionMapTy removeTrackedSubregions(TrackedRegionMapTy RegionMap, TrackedRegionMapTy::Factory &RegionMapFactory, const MemRegion *Region)
static bool isPotentiallyComparisionOpCall(const CallEvent &Call)
static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, CheckerContext &C)
static bool hasStdClassWithName(const CXXRecordDecl *RD, ArrayRef< llvm::StringLiteral > Names)
static bool isStdSmartPtr(const CXXRecordDecl *RD)
constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[]
constexpr llvm::StringLiteral STD_PTR_NAMES[]
C Language Family Type Representation.
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2122
bool isStdNamespace() const
bool isInStdNamespace() const
Definition DeclBase.cpp:450
DeclContext * getDeclContext()
Definition DeclBase.h:456
bool isIdentifier() const
Predicate functions for querying what type of name this is.
This represents one expression.
Definition Expr.h:112
QualType getType() const
Definition Expr.h:144
Represents a function declaration or definition.
Definition Decl.h:2018
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
Definition Decl.h:2951
OverloadedOperatorKind getOverloadedOperator() const
getOverloadedOperator - Which C++ overloaded operator this function represents, if any.
Definition Decl.cpp:4104
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition Decl.h:340
A (possibly-)qualified type.
Definition TypeBase.h:937
Represents a struct/union/class.
Definition Decl.h:4347
StringLiteral - This represents a string literal expression, e.g.
Definition Expr.h:1802
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
bool contains(const CallEvent &Call) const
bool matches(const CallEvent &Call) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
Represents an abstract call to a function or method along a particular path.
Definition CallEvent.h:152
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
const LangOptions & getLangOpts() const
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:550
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:97
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
virtual void printPretty(raw_ostream &os) const
Print the region for use in diagnostics.
virtual bool canPrintPretty() const
Returns true if this region can be printed in a user-friendly way.
BinaryOperatorKind GetBinaryOpUnsafe() const
bool isInteresting(SymbolRef sym) const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:56
const MemRegion * getAsRegion() const
Definition SVals.cpp:119
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition SVals.h:83
llvm::iterator_range< symbol_iterator > symbols() const
Definition SymExpr.h:107
void markLive(SymbolRef sym)
Unconditionally marks a symbol as live.
bool isLiveRegion(const MemRegion *region)
bool isMovedFrom(ProgramStateRef State, const MemRegion *Region)
Returns true if the object is known to have been recently std::moved.
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion)
Returns whether the smart pointer is null or not.
bool isStdSmartPtr(const CXXRecordDecl *RD)
bool isStdSmartPtrCall(const CallEvent &Call)
Returns true if the event call is on smart pointer.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition Store.h:50
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Definition SymExpr.h:133
OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK, bool IsBinary)
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
CFGBlock::ConstCFGElementRef ConstCFGElementRef
Definition CFG.h:1227
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
@ Type
The name was classified as a type.
Definition Sema.h:564
U cast(CodeGen::Address addr)
Definition Address.h:327
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30