Skip to content

Commit cd193f4

Browse files
authored
[analyzer] Remove redundant bug type DoubleDelete (#147542)
This commit removes the DoubleDelete bug type from `MallocChecker.cpp` because it's completely redundant with the `DoubleFree` bug (which is already used for all allocator families, including new/delete). This simplifies the code of the checker and prevents the potential confusion caused by two semantically equivalent and very similar, but not identical bug report messages.
1 parent 62f8377 commit cd193f4

File tree

2 files changed

+18
-44
lines changed

2 files changed

+18
-44
lines changed

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,6 @@ namespace {
346346
};
347347

348348
BUGTYPE_PROVIDER(DoubleFree, "Double free")
349-
// TODO: Remove DoubleDelete as a separate bug type and when it would be
350-
// emitted, emit DoubleFree reports instead. (Note that DoubleFree is already
351-
// used for all allocation families, not just malloc/free.)
352-
BUGTYPE_PROVIDER(DoubleDelete, "Double delete")
353349

354350
struct Leak : virtual public CheckerFrontend {
355351
// Leaks should not be reported if they are post-dominated by a sink:
@@ -410,8 +406,7 @@ class MallocChecker
410406
DynMemFrontend<DoubleFree, Leak, UseFree, BadFree, FreeAlloca, OffsetFree,
411407
UseZeroAllocated>
412408
MallocChecker;
413-
DynMemFrontend<DoubleFree, DoubleDelete, UseFree, BadFree, OffsetFree,
414-
UseZeroAllocated>
409+
DynMemFrontend<DoubleFree, UseFree, BadFree, OffsetFree, UseZeroAllocated>
415410
NewDeleteChecker;
416411
DynMemFrontend<Leak> NewDeleteLeaksChecker;
417412
DynMemFrontend<FreeAlloca, MismatchedDealloc> MismatchedDeallocatorChecker;
@@ -784,9 +779,6 @@ class MallocChecker
784779
void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
785780
const Stmt *S) const;
786781

787-
/// If in \p S \p Sym is being freed, check whether \p Sym was already freed.
788-
bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
789-
790782
/// Check if the function is known to free memory, or if it is
791783
/// "interesting" and should be modeled explicitly.
792784
///
@@ -848,8 +840,6 @@ class MallocChecker
848840
void HandleDoubleFree(CheckerContext &C, SourceRange Range, bool Released,
849841
SymbolRef Sym, SymbolRef PrevSym) const;
850842

851-
void HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
852-
853843
void HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
854844
SymbolRef Sym) const;
855845

@@ -2737,7 +2727,8 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range,
27372727
(Released ? "Attempt to free released memory"
27382728
: "Attempt to free non-owned memory"),
27392729
N);
2740-
R->addRange(Range);
2730+
if (Range.isValid())
2731+
R->addRange(Range);
27412732
R->markInteresting(Sym);
27422733
if (PrevSym)
27432734
R->markInteresting(PrevSym);
@@ -2746,26 +2737,6 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range,
27462737
}
27472738
}
27482739

2749-
void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
2750-
const DoubleDelete *Frontend = getRelevantFrontendAs<DoubleDelete>(C, Sym);
2751-
if (!Frontend)
2752-
return;
2753-
if (!Frontend->isEnabled()) {
2754-
C.addSink();
2755-
return;
2756-
}
2757-
2758-
if (ExplodedNode *N = C.generateErrorNode()) {
2759-
2760-
auto R = std::make_unique<PathSensitiveBugReport>(
2761-
Frontend->DoubleDeleteBug, "Attempt to delete released memory", N);
2762-
2763-
R->markInteresting(Sym);
2764-
R->addVisitor<MallocBugVisitor>(Sym);
2765-
C.emitReport(std::move(R));
2766-
}
2767-
}
2768-
27692740
void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
27702741
SymbolRef Sym) const {
27712742
const UseZeroAllocated *Frontend =
@@ -3136,10 +3107,22 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
31363107
return;
31373108
}
31383109

3110+
// If we see a `CXXDestructorCall` (that is, an _implicit_ destructor call)
3111+
// to a region that's symbolic and known to be already freed, then it must be
3112+
// implicitly triggered by a `delete` expression. In this situation we should
3113+
// emit a `DoubleFree` report _now_ (before entering the call to the
3114+
// destructor) because otherwise the destructor call can trigger a
3115+
// use-after-free bug (by accessing any member variable) and that would be
3116+
// (technically valid, but) less user-friendly report than the `DoubleFree`.
31393117
if (const auto *DC = dyn_cast<CXXDestructorCall>(&Call)) {
31403118
SymbolRef Sym = DC->getCXXThisVal().getAsSymbol();
3141-
if (!Sym || checkDoubleDelete(Sym, C))
3119+
if (!Sym)
31423120
return;
3121+
if (isReleased(Sym, C)) {
3122+
HandleDoubleFree(C, SourceRange(), /*Released=*/true, Sym,
3123+
/*PrevSym=*/nullptr);
3124+
return;
3125+
}
31433126
}
31443127

31453128
// We need to handle getline pre-conditions here before the pointed region
@@ -3321,15 +3304,6 @@ void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
33213304
}
33223305
}
33233306

3324-
bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
3325-
3326-
if (isReleased(Sym, C)) {
3327-
HandleDoubleDelete(C, Sym);
3328-
return true;
3329-
}
3330-
return false;
3331-
}
3332-
33333307
// Check if the location is a freed symbolic region.
33343308
void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
33353309
CheckerContext &C) const {

clang/test/Analysis/NewDelete-checker-test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ class DerefClass{
412412
void testDoubleDeleteClassInstance() {
413413
DerefClass *foo = new DerefClass();
414414
delete foo;
415-
delete foo; // newdelete-warning {{Attempt to delete released memory}}
415+
delete foo; // newdelete-warning {{Attempt to free released memory}}
416416
}
417417

418418
class EmptyClass{
@@ -424,7 +424,7 @@ class EmptyClass{
424424
void testDoubleDeleteEmptyClass() {
425425
EmptyClass *foo = new EmptyClass();
426426
delete foo;
427-
delete foo; // newdelete-warning {{Attempt to delete released memory}}
427+
delete foo; // newdelete-warning {{Attempt to free released memory}}
428428
}
429429

430430
struct Base {

0 commit comments

Comments
 (0)