@@ -446,15 +446,24 @@ class MallocChecker : public Checker<check::DeadSymbols,
446
446
// A symbol from when the primary region should have been reallocated.
447
447
SymbolRef FailedReallocSymbol;
448
448
449
+ // A C++ destructor stack frame in which memory was released. Used for
450
+ // miscellaneous false positive suppression.
451
+ const StackFrameContext *ReleaseDestructorLC;
452
+
449
453
bool IsLeak;
450
454
451
455
public:
452
456
MallocBugVisitor (SymbolRef S, bool isLeak = false )
453
- : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr ), IsLeak(isLeak) {}
457
+ : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr ),
458
+ ReleaseDestructorLC (nullptr ), IsLeak(isLeak) {}
459
+
460
+ static void *getTag () {
461
+ static int Tag = 0 ;
462
+ return &Tag;
463
+ }
454
464
455
465
void Profile (llvm::FoldingSetNodeID &ID) const override {
456
- static int X = 0 ;
457
- ID.AddPointer (&X);
466
+ ID.AddPointer (getTag ());
458
467
ID.AddPointer (Sym);
459
468
}
460
469
@@ -2822,6 +2831,32 @@ static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,
2822
2831
std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode (
2823
2832
const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
2824
2833
BugReport &BR) {
2834
+ const Stmt *S = PathDiagnosticLocation::getStmt (N);
2835
+ if (!S)
2836
+ return nullptr ;
2837
+
2838
+ const LocationContext *CurrentLC = N->getLocationContext ();
2839
+
2840
+ // If we find an atomic fetch_add or fetch_sub within the destructor in which
2841
+ // the pointer was released (before the release), this is likely a destructor
2842
+ // of a shared pointer.
2843
+ // Because we don't model atomics, and also because we don't know that the
2844
+ // original reference count is positive, we should not report use-after-frees
2845
+ // on objects deleted in such destructors. This can probably be improved
2846
+ // through better shared pointer modeling.
2847
+ if (ReleaseDestructorLC) {
2848
+ if (const auto *AE = dyn_cast<AtomicExpr>(S)) {
2849
+ AtomicExpr::AtomicOp Op = AE->getOp ();
2850
+ if (Op == AtomicExpr::AO__c11_atomic_fetch_add ||
2851
+ Op == AtomicExpr::AO__c11_atomic_fetch_sub) {
2852
+ if (ReleaseDestructorLC == CurrentLC ||
2853
+ ReleaseDestructorLC->isParentOf (CurrentLC)) {
2854
+ BR.markInvalid (getTag (), S);
2855
+ }
2856
+ }
2857
+ }
2858
+ }
2859
+
2825
2860
ProgramStateRef state = N->getState ();
2826
2861
ProgramStateRef statePrev = PrevN->getState ();
2827
2862
@@ -2830,10 +2865,6 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
2830
2865
if (!RS)
2831
2866
return nullptr ;
2832
2867
2833
- const Stmt *S = PathDiagnosticLocation::getStmt (N);
2834
- if (!S)
2835
- return nullptr ;
2836
-
2837
2868
// FIXME: We will eventually need to handle non-statement-based events
2838
2869
// (__attribute__((cleanup))).
2839
2870
@@ -2849,6 +2880,24 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
2849
2880
Msg = " Memory is released" ;
2850
2881
StackHint = new StackHintGeneratorForSymbol (Sym,
2851
2882
" Returning; memory was released" );
2883
+
2884
+ // See if we're releasing memory while inlining a destructor (or one of
2885
+ // its callees). If so, enable the atomic-related suppression within that
2886
+ // destructor (and all of its callees), which would kick in while visiting
2887
+ // other nodes (the visit order is from the bug to the graph root).
2888
+ for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent ()) {
2889
+ if (isa<CXXDestructorDecl>(LC->getDecl ())) {
2890
+ assert (!ReleaseDestructorLC &&
2891
+ " There can be only one release point!" );
2892
+ ReleaseDestructorLC = LC->getCurrentStackFrame ();
2893
+ // It is unlikely that releasing memory is delegated to a destructor
2894
+ // inside a destructor of a shared pointer, because it's fairly hard
2895
+ // to pass the information that the pointer indeed needs to be
2896
+ // released into it. So we're only interested in the innermost
2897
+ // destructor.
2898
+ break ;
2899
+ }
2900
+ }
2852
2901
} else if (isRelinquished (RS, RSPrev, S)) {
2853
2902
Msg = " Memory ownership is transferred" ;
2854
2903
StackHint = new StackHintGeneratorForSymbol (Sym, " " );
0 commit comments