[clang] [Clang SA]: add support for mismatched ownership_returns+ownership_takes calls for custom allocation classes (PR #98941)
Pavel Skripkin via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 17 10:16:31 PDT 2024
https://github.com/pskrgag updated https://github.com/llvm/llvm-project/pull/98941
>From 3d362cfb1e197713a51ce798996ff4308e7ab5aa Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 17 Jul 2024 16:41:20 +0300
Subject: [PATCH 1/6] clang/sema: disallow more than one 'onweship_takes' with
different classes
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++++
clang/lib/Sema/SemaDeclAttr.cpp | 10 ++++++++++
clang/test/Sema/attr-ownership.c | 4 ++++
3 files changed, 18 insertions(+)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0ea3677355169..77f830b3b4aee 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3328,6 +3328,10 @@ def err_ownership_returns_index_mismatch : Error<
"'ownership_returns' attribute index does not match; here it is %0">;
def note_ownership_returns_index_mismatch : Note<
"declared with index %0 here">;
+def err_ownership_takes_class_mismatch : Error<
+ "'ownership_takes' attribute class does not match; here it is '%0'">;
+def note_ownership_takes_class_mismatch : Note<
+ "declared with class '%0' here">;
def err_format_strftime_third_parameter : Error<
"strftime format attribute requires 3rd parameter to be 0">;
def err_format_attribute_not : Error<"format argument not a string type">;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 20f46c003a464..12f8f2ab8954d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1537,6 +1537,16 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
<< Idx.getSourceIndex() << Ex->getSourceRange();
return;
}
+ } else if (K == OwnershipAttr::Takes &&
+ I->getOwnKind() == OwnershipAttr::Takes) {
+ if (I->getModule()->getName() != ModuleName) {
+ S.Diag(I->getLocation(), diag::err_ownership_takes_class_mismatch)
+ << I->getModule()->getName();
+ S.Diag(AL.getLoc(), diag::note_ownership_takes_class_mismatch)
+ << ModuleName << Ex->getSourceRange();
+
+ return;
+ }
}
}
OwnershipArgs.push_back(Idx);
diff --git a/clang/test/Sema/attr-ownership.c b/clang/test/Sema/attr-ownership.c
index 8157ba7145a24..084624353315c 100644
--- a/clang/test/Sema/attr-ownership.c
+++ b/clang/test/Sema/attr-ownership.c
@@ -24,3 +24,7 @@ void f15(int, int)
void f16(int *i, int *j) __attribute__((ownership_holds(foo, 1))) __attribute__((ownership_holds(foo, 1))); // OK, same index
void f17(void*) __attribute__((ownership_takes(__, 1)));
void f18() __attribute__((ownership_takes(foo, 1))); // expected-warning {{'ownership_takes' attribute only applies to non-K&R-style functions}}
+
+int f19(void *)
+ __attribute__((ownership_takes(foo, 1))) // expected-error {{'ownership_takes' attribute class does not match; here it is 'foo'}}
+ __attribute__((ownership_takes(foo1, 1))); // expected-note {{declared with class 'foo1' here}}
>From 964262763a6d48410d4f83ba63d434d2824a2c87 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 17 Jul 2024 18:38:28 +0300
Subject: [PATCH 2/6] csa/MallocChecker: turn llvm_unreachable into asserts
---
.../lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index fe202c79ed620..3b558aa27e341 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1293,7 +1293,7 @@ void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call,
AF_CXXNewArray);
break;
default:
- llvm_unreachable("not a new/delete operator");
+ assert(false && "not a new/delete operator");
}
C.addTransition(State);
@@ -1490,7 +1490,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
return State;
}
} else
- llvm_unreachable("not a CallExpr or CXXNewExpr");
+ assert(false && "not a CallExpr or CXXNewExpr");
assert(Arg);
@@ -1925,7 +1925,7 @@ static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
case AF_IfNameIndex: os << "'if_nameindex()'"; return;
case AF_InnerBuffer: os << "container-specific allocator"; return;
case AF_Alloca:
- case AF_None: llvm_unreachable("not a deallocation expression");
+ case AF_None: assert(false && "not a deallocation expression");
}
}
@@ -1937,7 +1937,7 @@ static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
case AF_InnerBuffer: os << "container-specific deallocator"; return;
case AF_Alloca:
- case AF_None: llvm_unreachable("suspicious argument");
+ case AF_None: assert(false && "suspicious argument");
}
}
@@ -2145,10 +2145,10 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
return std::nullopt;
}
case AF_None: {
- llvm_unreachable("no family");
+ assert(false && "no family");
}
}
- llvm_unreachable("unhandled family");
+ assert(false && "unhandled family");
}
std::optional<MallocChecker::CheckKind>
@@ -3528,7 +3528,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
break;
}
case AF_None:
- llvm_unreachable("Unhandled allocation family!");
+ assert(false && "Unhandled allocation family!");
}
// See if we're releasing memory while inlining a destructor
>From bea0ff49a2a90d8a935f652341c55815c38ffaa6 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 17 Jul 2024 18:43:50 +0300
Subject: [PATCH 3/6] csa/MallocChecker: add support for custom allocation
classes
Add support for custom allocation classes that could be specified by
ownership_returns attribute.
This patch adds basic support, so new class is not contructed anywhere
and handled as an error everywhere.
---
.../StaticAnalyzer/Checkers/MallocChecker.cpp | 299 +++++++++++-------
1 file changed, 181 insertions(+), 118 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 3b558aa27e341..c5f89abb11161 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -103,14 +103,41 @@ using namespace std::placeholders;
namespace {
// Used to check correspondence between allocators and deallocators.
-enum AllocationFamily {
+enum AllocationFamilyKind {
AF_None,
AF_Malloc,
AF_CXXNew,
AF_CXXNewArray,
AF_IfNameIndex,
AF_Alloca,
- AF_InnerBuffer
+ AF_InnerBuffer,
+ AF_Custom,
+};
+
+struct AllocationFamily {
+ AllocationFamilyKind Kind;
+ std::optional<StringRef> CustomName;
+
+ explicit AllocationFamily(AllocationFamilyKind kind,
+ std::optional<StringRef> name = std::nullopt)
+ : Kind(kind), CustomName(name) {
+ assert(kind != AF_Custom || name != std::nullopt);
+ }
+
+ bool operator==(const AllocationFamily &Other) const {
+ return std::tie(Kind, CustomName) == std::tie(Other.Kind, Other.CustomName);
+ }
+
+ bool operator!=(const AllocationFamily &Other) const {
+ return !(*this == Other);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(Kind);
+
+ if (Kind == AF_Custom)
+ ID.AddString(CustomName.value());
+ }
};
} // end of anonymous namespace
@@ -158,7 +185,7 @@ class RefState {
RefState(Kind k, const Stmt *s, AllocationFamily family)
: S(s), K(k), Family(family) {
- assert(family != AF_None);
+ assert(family.Kind != AF_None);
}
public:
@@ -194,7 +221,7 @@ class RefState {
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(K);
ID.AddPointer(S);
- ID.AddInteger(Family);
+ Family.Profile(ID);
}
LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
@@ -899,7 +926,7 @@ class MallocBugVisitor final : public BugReporterVisitor {
bool IsReleased =
(RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased());
assert(!IsReleased || (isa_and_nonnull<CallExpr, CXXDeleteExpr>(Stmt)) ||
- (!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer));
+ (!Stmt && RSCurr->getAllocationFamily().Kind == AF_InnerBuffer));
return IsReleased;
}
@@ -1122,7 +1149,7 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
if (TrueState && !FalseState) {
SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);
return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState,
- AF_Malloc);
+ AllocationFamily(AF_Malloc));
}
return std::nullopt;
@@ -1143,7 +1170,7 @@ void MallocChecker::checkBasicAlloc(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
- AF_Malloc);
+ AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(Call, 0, State);
C.addTransition(State);
}
@@ -1157,7 +1184,7 @@ void MallocChecker::checkKernelMalloc(const CallEvent &Call,
State = *MaybeState;
else
State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
- AF_Malloc);
+ AllocationFamily(AF_Malloc));
C.addTransition(State);
}
@@ -1194,7 +1221,8 @@ void MallocChecker::checkRealloc(const CallEvent &Call, CheckerContext &C,
return;
ProgramStateRef State = C.getState();
- State = ReallocMemAux(C, Call, ShouldFreeOnFail, State, AF_Malloc);
+ State = ReallocMemAux(C, Call, ShouldFreeOnFail, State,
+ AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
@@ -1214,7 +1242,7 @@ void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const {
if (suppressDeallocationsInSuspiciousContexts(Call, C))
return;
State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_Malloc);
+ AllocationFamily(AF_Malloc));
C.addTransition(State);
}
@@ -1222,7 +1250,7 @@ void MallocChecker::checkAlloca(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
- AF_Alloca);
+ AllocationFamily(AF_Alloca));
State = ProcessZeroAllocCheck(Call, 0, State);
C.addTransition(State);
}
@@ -1233,7 +1261,7 @@ void MallocChecker::checkStrdup(const CallEvent &Call,
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
- State = MallocUpdateRefState(C, CE, State, AF_Malloc);
+ State = MallocUpdateRefState(C, CE, State, AllocationFamily(AF_Malloc));
C.addTransition(State);
}
@@ -1243,8 +1271,8 @@ void MallocChecker::checkIfNameIndex(const CallEvent &Call,
ProgramStateRef State = C.getState();
// Should we model this differently? We can allocate a fixed number of
// elements with zeros in the last one.
- State =
- MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State, AF_IfNameIndex);
+ State = MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State,
+ AllocationFamily(AF_IfNameIndex));
C.addTransition(State);
}
@@ -1254,7 +1282,7 @@ void MallocChecker::checkIfFreeNameIndex(const CallEvent &Call,
ProgramStateRef State = C.getState();
bool IsKnownToBeAllocatedMemory = false;
State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_IfNameIndex);
+ AllocationFamily(AF_IfNameIndex));
C.addTransition(State);
}
@@ -1275,22 +1303,22 @@ void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call,
const FunctionDecl *FD = C.getCalleeDecl(CE);
switch (FD->getOverloadedOperator()) {
case OO_New:
- State =
- MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, AF_CXXNew);
+ State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,
+ AllocationFamily(AF_CXXNew));
State = ProcessZeroAllocCheck(Call, 0, State);
break;
case OO_Array_New:
State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNewArray);
+ AllocationFamily(AF_CXXNewArray));
State = ProcessZeroAllocCheck(Call, 0, State);
break;
case OO_Delete:
State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_CXXNew);
+ AllocationFamily(AF_CXXNew));
break;
case OO_Array_Delete:
State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_CXXNewArray);
+ AllocationFamily(AF_CXXNewArray));
break;
default:
assert(false && "not a new/delete operator");
@@ -1304,7 +1332,8 @@ void MallocChecker::checkGMalloc0(const CallEvent &Call,
ProgramStateRef State = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State, AF_Malloc);
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State,
+ AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(Call, 0, State);
C.addTransition(State);
}
@@ -1312,8 +1341,8 @@ void MallocChecker::checkGMalloc0(const CallEvent &Call,
void MallocChecker::checkGMemdup(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- State =
- MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State, AF_Malloc);
+ State = MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State,
+ AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
@@ -1323,7 +1352,8 @@ void MallocChecker::checkGMallocN(const CallEvent &Call,
ProgramStateRef State = C.getState();
SVal Init = UndefinedVal();
SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
- State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc);
+ State = MallocMemAux(C, Call, TotalSize, Init, State,
+ AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(Call, 0, State);
State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
@@ -1335,7 +1365,8 @@ void MallocChecker::checkGMallocN0(const CallEvent &Call,
SValBuilder &SB = C.getSValBuilder();
SVal Init = SB.makeZeroVal(SB.getContext().CharTy);
SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
- State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc);
+ State = MallocMemAux(C, Call, TotalSize, Init, State,
+ AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(Call, 0, State);
State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
@@ -1365,7 +1396,8 @@ void MallocChecker::preGetdelim(const CallEvent &Call,
// of reporting any violation of the preconditions.
bool IsKnownToBeAllocated = false;
State = FreeMemAux(C, Call.getArgExpr(0), Call, State, false,
- IsKnownToBeAllocated, AF_Malloc, false, LinePtr);
+ IsKnownToBeAllocated, AllocationFamily(AF_Malloc), false,
+ LinePtr);
if (State)
C.addTransition(State);
}
@@ -1394,13 +1426,15 @@ void MallocChecker::checkGetdelim(const CallEvent &Call,
return;
State = setDynamicExtent(State, LinePtr->getAsRegion(), *Size, SVB);
- C.addTransition(MallocUpdateRefState(C, CE, State, AF_Malloc, *LinePtr));
+ C.addTransition(MallocUpdateRefState(C, CE, State,
+ AllocationFamily(AF_Malloc), *LinePtr));
}
void MallocChecker::checkReallocN(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State, AF_Malloc,
+ State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State,
+ AllocationFamily(AF_Malloc),
/*SuffixWithN=*/true);
State = ProcessZeroAllocCheck(Call, 1, State);
State = ProcessZeroAllocCheck(Call, 2, State);
@@ -1598,7 +1632,8 @@ MallocChecker::processNewAllocation(const CXXAllocatorCall &Call,
SVal Target = Call.getObjectUnderConstruction();
if (Call.getOriginExpr()->isArray()) {
if (auto SizeEx = NE->getArraySize())
- checkTaintedness(C, Call, C.getSVal(*SizeEx), State, AF_CXXNewArray);
+ checkTaintedness(C, Call, C.getSVal(*SizeEx), State,
+ AllocationFamily(AF_CXXNewArray));
}
State = MallocUpdateRefState(C, NE, State, Family, Target);
@@ -1611,7 +1646,8 @@ void MallocChecker::checkNewAllocator(const CXXAllocatorCall &Call,
if (!C.wasInlined) {
ProgramStateRef State = processNewAllocation(
Call, C,
- (Call.getOriginExpr()->isArray() ? AF_CXXNewArray : AF_CXXNew));
+ AllocationFamily(Call.getOriginExpr()->isArray() ? AF_CXXNewArray
+ : AF_CXXNew));
C.addTransition(State);
}
}
@@ -1655,10 +1691,10 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
return;
bool IsKnownToBeAllocatedMemory;
- ProgramStateRef State =
- FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),
- /*Hold=*/true, IsKnownToBeAllocatedMemory, AF_Malloc,
- /*ReturnsNullOnFailure=*/true);
+ ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),
+ /*Hold=*/true, IsKnownToBeAllocatedMemory,
+ AllocationFamily(AF_Malloc),
+ /*ReturnsNullOnFailure=*/true);
C.addTransition(State);
}
@@ -1676,9 +1712,10 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
if (!Att->args().empty()) {
return MallocMemAux(C, Call,
Call.getArgExpr(Att->args_begin()->getASTIndex()),
- UndefinedVal(), State, AF_Malloc);
+ UndefinedVal(), State, AllocationFamily(AF_Malloc));
}
- return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc);
+ return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State,
+ AllocationFamily(AF_Malloc));
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
@@ -1768,10 +1805,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
unsigned Count = C.blockCount();
SValBuilder &SVB = C.getSValBuilder();
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
- DefinedSVal RetVal =
- ((Family == AF_Alloca) ? SVB.getAllocaRegionVal(CE, LCtx, Count)
- : SVB.getConjuredHeapSymbolVal(CE, LCtx, Count)
- .castAs<DefinedSVal>());
+ DefinedSVal RetVal = ((Family.Kind == AF_Alloca)
+ ? SVB.getAllocaRegionVal(CE, LCtx, Count)
+ : SVB.getConjuredHeapSymbolVal(CE, LCtx, Count)
+ .castAs<DefinedSVal>());
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
// Fill the region with the initialization value.
@@ -1781,7 +1818,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
if (Size.isUndef())
Size = UnknownVal();
- checkTaintedness(C, Call, Size, State, AF_Malloc);
+ checkTaintedness(C, Call, Size, State, AllocationFamily(AF_Malloc));
// Set the region's extent.
State = setDynamicExtent(State, RetVal.getAsRegion(),
@@ -1833,13 +1870,11 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
if (Att->getModule()->getName() != "malloc")
return nullptr;
- bool IsKnownToBeAllocated = false;
-
for (const auto &Arg : Att->args()) {
ProgramStateRef StateI =
FreeMemAux(C, Call, State, Arg.getASTIndex(),
Att->getOwnKind() == OwnershipAttr::Holds,
- IsKnownToBeAllocated, AF_Malloc);
+ IsKnownToBeAllocated, AllocationFamily(AF_Malloc));
if (StateI)
State = StateI;
}
@@ -1918,26 +1953,50 @@ static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {
static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
- switch(Family) {
- case AF_Malloc: os << "malloc()"; return;
- case AF_CXXNew: os << "'new'"; return;
- case AF_CXXNewArray: os << "'new[]'"; return;
- case AF_IfNameIndex: os << "'if_nameindex()'"; return;
- case AF_InnerBuffer: os << "container-specific allocator"; return;
- case AF_Alloca:
- case AF_None: assert(false && "not a deallocation expression");
+ switch (Family.Kind) {
+ case AF_Malloc:
+ os << "malloc()";
+ return;
+ case AF_CXXNew:
+ os << "'new'";
+ return;
+ case AF_CXXNewArray:
+ os << "'new[]'";
+ return;
+ case AF_IfNameIndex:
+ os << "'if_nameindex()'";
+ return;
+ case AF_InnerBuffer:
+ os << "container-specific allocator";
+ return;
+ case AF_Alloca:
+ case AF_Custom:
+ case AF_None:
+ assert(false && "not a deallocation expression");
}
}
static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
- switch(Family) {
- case AF_Malloc: os << "free()"; return;
- case AF_CXXNew: os << "'delete'"; return;
- case AF_CXXNewArray: os << "'delete[]'"; return;
- case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
- case AF_InnerBuffer: os << "container-specific deallocator"; return;
- case AF_Alloca:
- case AF_None: assert(false && "suspicious argument");
+ switch (Family.Kind) {
+ case AF_Malloc:
+ os << "free()";
+ return;
+ case AF_CXXNew:
+ os << "'delete'";
+ return;
+ case AF_CXXNewArray:
+ os << "'delete[]'";
+ return;
+ case AF_IfNameIndex:
+ os << "'if_freenameindex()'";
+ return;
+ case AF_InnerBuffer:
+ os << "container-specific deallocator";
+ return;
+ case AF_Alloca:
+ case AF_Custom:
+ case AF_None:
+ assert(false && "not a deallocation expression");
}
}
@@ -1991,7 +2050,7 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
// code. In that case, the ZERO_SIZE_PTR defines a special value used for a
// zero-sized memory block which is allowed to be freed, despite not being a
// null pointer.
- if (Family != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal))
+ if (Family.Kind != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal))
HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
Family);
return nullptr;
@@ -2041,7 +2100,7 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
if (RsBase) {
// Memory returned by alloca() shouldn't be freed.
- if (RsBase->getAllocationFamily() == AF_Alloca) {
+ if (RsBase->getAllocationFamily().Kind == AF_Alloca) {
HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
return nullptr;
}
@@ -2119,7 +2178,7 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
std::optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(AllocationFamily Family,
bool IsALeakCheck) const {
- switch (Family) {
+ switch (Family.Kind) {
case AF_Malloc:
case AF_Alloca:
case AF_IfNameIndex: {
@@ -2144,6 +2203,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
return CK_InnerPointerChecker;
return std::nullopt;
}
+ case AF_Custom:
case AF_None: {
assert(false && "no family");
}
@@ -2465,7 +2525,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range,
auto R = std::make_unique<PathSensitiveBugReport>(
*BT_UseFree[*CheckKind],
- AF == AF_InnerBuffer
+ AF.Kind == AF_InnerBuffer
? "Inner pointer of container used after re/deallocation"
: "Use of memory after it is freed",
N);
@@ -2474,7 +2534,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range,
R->addRange(Range);
R->addVisitor<MallocBugVisitor>(Sym);
- if (AF == AF_InnerBuffer)
+ if (AF.Kind == AF_InnerBuffer)
R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym));
C.emitReport(std::move(R));
@@ -2733,7 +2793,8 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C,
SVal TotalSize =
evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
- return MallocMemAux(C, Call, TotalSize, zeroVal, State, AF_Malloc);
+ return MallocMemAux(C, Call, TotalSize, zeroVal, State,
+ AllocationFamily(AF_Malloc));
}
MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
@@ -2788,7 +2849,7 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
assert(RS && "cannot leak an untracked symbol");
AllocationFamily Family = RS->getAllocationFamily();
- if (Family == AF_Alloca)
+ if (Family.Kind == AF_Alloca)
return;
std::optional<MallocChecker::CheckKind> CheckKind =
@@ -2915,9 +2976,10 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
ProgramStateRef State = C.getState();
bool IsKnownToBeAllocated;
- State = FreeMemAux(C, DE->getArgument(), Call, State,
- /*Hold*/ false, IsKnownToBeAllocated,
- (DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew));
+ State = FreeMemAux(
+ C, DE->getArgument(), Call, State,
+ /*Hold*/ false, IsKnownToBeAllocated,
+ AllocationFamily(DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew));
C.addTransition(State);
return;
@@ -3349,8 +3411,8 @@ ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,
}
static bool checkIfNewOrNewArrayFamily(const RefState *RS) {
- return (RS->getAllocationFamily() == AF_CXXNewArray ||
- RS->getAllocationFamily() == AF_CXXNew);
+ return (RS->getAllocationFamily().Kind == AF_CXXNewArray ||
+ RS->getAllocationFamily().Kind == AF_CXXNew);
}
ProgramStateRef MallocChecker::checkPointerEscapeAux(
@@ -3431,7 +3493,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
const Stmt *S = N->getStmtForDiagnostics();
// When dealing with containers, we sometimes want to give a note
// even if the statement is missing.
- if (!S && (!RSCurr || RSCurr->getAllocationFamily() != AF_InnerBuffer))
+ if (!S && (!RSCurr || RSCurr->getAllocationFamily().Kind != AF_InnerBuffer))
return nullptr;
const LocationContext *CurrentLC = N->getLocationContext();
@@ -3483,52 +3545,53 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
Sym, "Returned allocated memory");
} else if (isReleased(RSCurr, RSPrev, S)) {
const auto Family = RSCurr->getAllocationFamily();
- switch (Family) {
- case AF_Alloca:
- case AF_Malloc:
- case AF_CXXNew:
- case AF_CXXNewArray:
- case AF_IfNameIndex:
- Msg = "Memory is released";
+ switch (Family.Kind) {
+ case AF_Alloca:
+ case AF_Malloc:
+ case AF_CXXNew:
+ case AF_CXXNewArray:
+ case AF_IfNameIndex:
+ Msg = "Memory is released";
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+ Sym, "Returning; memory was released");
+ break;
+ case AF_InnerBuffer: {
+ const MemRegion *ObjRegion =
+ allocation_state::getContainerObjRegion(statePrev, Sym);
+ const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
+ QualType ObjTy = TypedRegion->getValueType();
+ OS << "Inner buffer of '" << ObjTy << "' ";
+
+ if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
+ OS << "deallocated by call to destructor";
StackHint = std::make_unique<StackHintGeneratorForSymbol>(
- Sym, "Returning; memory was released");
- break;
- case AF_InnerBuffer: {
- const MemRegion *ObjRegion =
- allocation_state::getContainerObjRegion(statePrev, Sym);
- const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
- QualType ObjTy = TypedRegion->getValueType();
- OS << "Inner buffer of '" << ObjTy << "' ";
-
- if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
- OS << "deallocated by call to destructor";
- StackHint = std::make_unique<StackHintGeneratorForSymbol>(
- Sym, "Returning; inner buffer was deallocated");
- } else {
- OS << "reallocated by call to '";
- const Stmt *S = RSCurr->getStmt();
- if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
- OS << MemCallE->getMethodDecl()->getDeclName();
- } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
- OS << OpCallE->getDirectCallee()->getDeclName();
- } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
- auto &CEMgr = BRC.getStateManager().getCallEventManager();
- CallEventRef<> Call =
- CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
- if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
- OS << D->getDeclName();
- else
- OS << "unknown";
- }
- OS << "'";
- StackHint = std::make_unique<StackHintGeneratorForSymbol>(
- Sym, "Returning; inner buffer was reallocated");
+ Sym, "Returning; inner buffer was deallocated");
+ } else {
+ OS << "reallocated by call to '";
+ const Stmt *S = RSCurr->getStmt();
+ if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
+ OS << MemCallE->getMethodDecl()->getDeclName();
+ } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
+ OS << OpCallE->getDirectCallee()->getDeclName();
+ } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
+ auto &CEMgr = BRC.getStateManager().getCallEventManager();
+ CallEventRef<> Call =
+ CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
+ if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
+ OS << D->getDeclName();
+ else
+ OS << "unknown";
}
- Msg = OS.str();
- break;
+ OS << "'";
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+ Sym, "Returning; inner buffer was reallocated");
}
- case AF_None:
- assert(false && "Unhandled allocation family!");
+ Msg = OS.str();
+ break;
+ }
+ case AF_Custom:
+ case AF_None:
+ assert(false && "Unhandled allocation family!");
}
// See if we're releasing memory while inlining a destructor
@@ -3603,7 +3666,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
// Generate the extra diagnostic.
PathDiagnosticLocation Pos;
if (!S) {
- assert(RSCurr->getAllocationFamily() == AF_InnerBuffer);
+ assert(RSCurr->getAllocationFamily().Kind == AF_InnerBuffer);
auto PostImplCall = N->getLocation().getAs<PostImplicitCall>();
if (!PostImplCall)
return nullptr;
@@ -3650,7 +3713,7 @@ namespace allocation_state {
ProgramStateRef
markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
- AllocationFamily Family = AF_InnerBuffer;
+ AllocationFamily Family(AF_InnerBuffer);
return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin));
}
>From 44df42ee5aaed4c8a66e52c52f45e6979eeab156 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 17 Jul 2024 18:49:13 +0300
Subject: [PATCH 4/6] csa/MismatchedDeallocator: handle custom allocation
clasess
Patch introduces support for custom allocation classes in
MismatchedDeallocator.
Patch preserves previous behavior, in which 'malloc' class was handled
as AF_Malloc type.
---
.../StaticAnalyzer/Checkers/MallocChecker.cpp | 64 +++++++++++++++----
1 file changed, 52 insertions(+), 12 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index c5f89abb11161..e714719cd29d7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -122,6 +122,14 @@ struct AllocationFamily {
std::optional<StringRef> name = std::nullopt)
: Kind(kind), CustomName(name) {
assert(kind != AF_Custom || name != std::nullopt);
+
+ // Preseve previous behavior when "malloc" class means AF_Malloc
+ if (Kind == AF_Malloc && CustomName) {
+ if (CustomName.value() == "malloc")
+ CustomName = std::nullopt;
+ else
+ Kind = AF_Custom;
+ }
}
bool operator==(const AllocationFamily &Other) const {
@@ -1706,16 +1714,15 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
if (!State)
return nullptr;
- if (Att->getModule()->getName() != "malloc")
- return nullptr;
+ auto attrClassName = Att->getModule()->getName();
+ auto Family = AllocationFamily(AF_Malloc, attrClassName);
if (!Att->args().empty()) {
return MallocMemAux(C, Call,
Call.getArgExpr(Att->args_begin()->getASTIndex()),
- UndefinedVal(), State, AllocationFamily(AF_Malloc));
+ UndefinedVal(), State, Family);
}
- return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State,
- AllocationFamily(AF_Malloc));
+ return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, Family);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
@@ -1867,14 +1874,16 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
if (!State)
return nullptr;
- if (Att->getModule()->getName() != "malloc")
- return nullptr;
+ auto attrClassName = Att->getModule()->getName();
+ auto Family = AllocationFamily(AF_Malloc, attrClassName);
+
+ bool IsKnownToBeAllocated = false;
for (const auto &Arg : Att->args()) {
ProgramStateRef StateI =
FreeMemAux(C, Call, State, Arg.getASTIndex(),
Att->getOwnKind() == OwnershipAttr::Holds,
- IsKnownToBeAllocated, AllocationFamily(AF_Malloc));
+ IsKnownToBeAllocated, Family);
if (StateI)
State = StateI;
}
@@ -1912,6 +1921,30 @@ static bool didPreviousFreeFail(ProgramStateRef State,
return false;
}
+static void printOwnershipTakesList(raw_ostream &os, CheckerContext &C,
+ const Expr *E) {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (!FD)
+ return;
+
+ if (!FD->hasAttrs())
+ return;
+
+ // Only one ownership_takes attribute is allowed
+ for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
+ OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
+
+ if (OwnKind != OwnershipAttr::Takes)
+ continue;
+
+ os << ", which takes ownership of "
+ << '\'' << I->getModule()->getName() << '\'';
+ break;
+ }
+ }
+}
+
static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
// FIXME: This doesn't handle indirect calls.
@@ -1969,8 +2002,10 @@ static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
case AF_InnerBuffer:
os << "container-specific allocator";
return;
- case AF_Alloca:
case AF_Custom:
+ os << Family.CustomName.value();
+ return;
+ case AF_Alloca:
case AF_None:
assert(false && "not a deallocation expression");
}
@@ -1993,8 +2028,11 @@ static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
case AF_InnerBuffer:
os << "container-specific deallocator";
return;
- case AF_Alloca:
case AF_Custom:
+ os << "function that takes ownership of '" << Family.CustomName.value()
+ << "\'";
+ return;
+ case AF_Alloca:
case AF_None:
assert(false && "not a deallocation expression");
}
@@ -2181,6 +2219,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
switch (Family.Kind) {
case AF_Malloc:
case AF_Alloca:
+ case AF_Custom:
case AF_IfNameIndex: {
if (ChecksEnabled[CK_MallocChecker])
return CK_MallocChecker;
@@ -2203,7 +2242,6 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
return CK_InnerPointerChecker;
return std::nullopt;
}
- case AF_Custom:
case AF_None: {
assert(false && "no family");
}
@@ -2433,6 +2471,8 @@ void MallocChecker::HandleMismatchedDealloc(CheckerContext &C,
if (printMemFnName(DeallocOs, C, DeallocExpr))
os << ", not " << DeallocOs.str();
+
+ printOwnershipTakesList(os, C, DeallocExpr);
}
auto R = std::make_unique<PathSensitiveBugReport>(*BT_MismatchedDealloc,
@@ -3548,6 +3588,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
switch (Family.Kind) {
case AF_Alloca:
case AF_Malloc:
+ case AF_Custom:
case AF_CXXNew:
case AF_CXXNewArray:
case AF_IfNameIndex:
@@ -3589,7 +3630,6 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
Msg = OS.str();
break;
}
- case AF_Custom:
case AF_None:
assert(false && "Unhandled allocation family!");
}
>From 7fd70802d01dc232c994e6b88ce972f25c7c725a Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 17 Jul 2024 16:52:41 +0300
Subject: [PATCH 5/6] csa/MallocChecker: quote funtion names in error output
---
.../StaticAnalyzer/Checkers/MallocChecker.cpp | 13 +++--
.../expected-plists/plist-macros.cpp.plist | 6 +--
...Malloc+MismatchedDeallocator+NewDelete.cpp | 10 ++--
.../MismatchedDeallocator-checker-test.mm | 36 +++++++-------
.../test/Analysis/NewDelete-intersections.mm | 6 +--
clang/test/Analysis/free.c | 24 +++++-----
clang/test/Analysis/free.cpp | 48 +++++++++----------
clang/test/Analysis/getline-alloc.c | 8 ++--
clang/test/Analysis/kmalloc-linux.c | 2 +-
clang/test/Analysis/malloc-fnptr-plist.c | 2 +-
clang/test/Analysis/malloc-std-namespace.cpp | 4 +-
clang/test/Analysis/malloc.c | 36 +++++++-------
clang/test/Analysis/malloc.mm | 2 +-
clang/test/Analysis/plist-macros.cpp | 2 +-
clang/test/Analysis/weak-functions.c | 2 +-
15 files changed, 102 insertions(+), 99 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index e714719cd29d7..b0af2645ce0a0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1952,9 +1952,12 @@ static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {
if (!FD)
return false;
- os << *FD;
+ os << '\'' << *FD;
+
if (!FD->isOverloadedOperator())
os << "()";
+
+ os << '\'';
return true;
}
@@ -1988,7 +1991,7 @@ static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
switch (Family.Kind) {
case AF_Malloc:
- os << "malloc()";
+ os << "'malloc()'";
return;
case AF_CXXNew:
os << "'new'";
@@ -2014,7 +2017,7 @@ static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
switch (Family.Kind) {
case AF_Malloc:
- os << "free()";
+ os << "'free()'";
return;
case AF_CXXNew:
os << "'delete'";
@@ -2414,11 +2417,11 @@ void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal,
if (ExplodedNode *N = C.generateErrorNode()) {
if (!BT_FreeAlloca[*CheckKind])
BT_FreeAlloca[*CheckKind].reset(new BugType(
- CheckNames[*CheckKind], "Free alloca()", categories::MemoryError));
+ CheckNames[*CheckKind], "Free 'alloca()'", categories::MemoryError));
auto R = std::make_unique<PathSensitiveBugReport>(
*BT_FreeAlloca[*CheckKind],
- "Memory allocated by alloca() should not be deallocated", N);
+ "Memory allocated by 'alloca()' should not be deallocated", N);
R->markInteresting(ArgVal.getAsRegion());
R->addRange(Range);
C.emitReport(std::move(R));
diff --git a/clang/test/Analysis/Inputs/expected-plists/plist-macros.cpp.plist b/clang/test/Analysis/Inputs/expected-plists/plist-macros.cpp.plist
index 9981fc6a1f517..13645d47f8cdc 100644
--- a/clang/test/Analysis/Inputs/expected-plists/plist-macros.cpp.plist
+++ b/clang/test/Analysis/Inputs/expected-plists/plist-macros.cpp.plist
@@ -130,12 +130,12 @@
</array>
<key>depth</key><integer>0</integer>
<key>extended_message</key>
- <string>Memory allocated by malloc() should be deallocated by free(), not 'delete'</string>
+ <string>Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'</string>
<key>message</key>
- <string>Memory allocated by malloc() should be deallocated by free(), not 'delete'</string>
+ <string>Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'</string>
</dict>
</array>
- <key>description</key><string>Memory allocated by malloc() should be deallocated by free(), not 'delete'</string>
+ <key>description</key><string>Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'</string>
<key>category</key><string>Memory error</string>
<key>type</key><string>Bad deallocator</string>
<key>check_name</key><string>unix.MismatchedDeallocator</string>
diff --git a/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp b/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp
index b5e47b3355da3..6c20b4ba53dae 100644
--- a/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp
+++ b/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp
@@ -24,12 +24,12 @@ void testMallocUseAfterFree() {
void testMallocBadFree() {
int i;
- free(&i); // expected-warning{{Argument to free() is the address of the local variable 'i', which is not memory allocated by malloc()}}
+ free(&i); // expected-warning{{Argument to 'free()' is the address of the local variable 'i', which is not memory allocated by 'malloc()'}}
}
void testMallocOffsetFree() {
int *p = (int *)malloc(sizeof(int));
- free(++p); // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}}
+ free(++p); // expected-warning{{Argument to 'free()' is offset by 4 bytes from the start of memory allocated by 'malloc()'}}
}
//-----------------------------------------------------------------
@@ -37,7 +37,7 @@ void testMallocOffsetFree() {
//-----------------------------------------------------------------
void testMismatchedDeallocator() {
int *x = (int *)malloc(sizeof(int));
- delete x; // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not 'delete'}}
+ delete x; // expected-warning{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'}}
}
//----------------------------------------------------------------
@@ -69,7 +69,7 @@ void testNewBadFree() {
void testNewOffsetFree() {
int *p = new int;
- operator delete(++p); // expected-warning{{Argument to operator delete is offset by 4 bytes from the start of memory allocated by 'new'}}
+ operator delete(++p); // expected-warning{{Argument to 'operator delete' is offset by 4 bytes from the start of memory allocated by 'new'}}
}
//----------------------------------------------------------------
@@ -88,7 +88,7 @@ void testMismatchedChangePtrThroughCall() {
void testMismatchedChangePointeeThroughCall() {
int *p = (int*)malloc(sizeof(int)*4);
changePointee(p);
- delete p; // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not 'delete'}}
+ delete p; // expected-warning{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'}}
}
void testShouldReportDoubleFreeNotMismatched() {
diff --git a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
index 013d677e515cf..bbf24837f9da8 100644
--- a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
+++ b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
@@ -21,79 +21,79 @@
//--------------- test malloc family
void testMalloc1() {
int *p = (int *)malloc(sizeof(int));
- delete p; // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not 'delete'}}
+ delete p; // expected-warning{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'}}
}
void testMalloc2() {
int *p = (int *)malloc(8);
int *q = (int *)realloc(p, 16);
- delete q; // expected-warning{{Memory allocated by realloc() should be deallocated by free(), not 'delete'}}
+ delete q; // expected-warning{{Memory allocated by 'realloc()' should be deallocated by 'free()', not 'delete'}}
}
void testMalloc3() {
int *p = (int *)calloc(1, sizeof(int));
- delete p; // expected-warning{{Memory allocated by calloc() should be deallocated by free(), not 'delete'}}
+ delete p; // expected-warning{{Memory allocated by 'calloc()' should be deallocated by 'free()', not 'delete'}}
}
void testMalloc4(const char *s) {
char *p = strdup(s);
- delete p; // expected-warning{{Memory allocated by strdup() should be deallocated by free(), not 'delete'}}
+ delete p; // expected-warning{{Memory allocated by 'strdup()' should be deallocated by 'free()', not 'delete'}}
}
void testMalloc5() {
int *p = (int *)my_malloc(sizeof(int));
- delete p; // expected-warning{{Memory allocated by my_malloc() should be deallocated by free(), not 'delete'}}
+ delete p; // expected-warning{{Memory allocated by 'my_malloc()' should be deallocated by 'free()', not 'delete'}}
}
void testMalloc6() {
int *p = (int *)malloc(sizeof(int));
- operator delete(p); // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not operator delete}}
+ operator delete(p); // expected-warning{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'operator delete'}}
}
void testMalloc7() {
int *p = (int *)malloc(sizeof(int));
- delete[] p; // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not 'delete[]'}}
+ delete[] p; // expected-warning{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete[]'}}
}
void testMalloc8() {
int *p = (int *)malloc(sizeof(int));
- operator delete[](p); // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not operator delete[]}}
+ operator delete[](p); // expected-warning{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'operator delete[]'}}
}
void testAlloca() {
int *p = (int *)__builtin_alloca(sizeof(int));
- delete p; // expected-warning{{Memory allocated by alloca() should not be deallocated}}
+ delete p; // expected-warning{{Memory allocated by 'alloca()' should not be deallocated}}
}
//--------------- test new family
void testNew1() {
int *p = new int;
- free(p); // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not free()}}
+ free(p); // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not 'free()'}}
}
void testNew2() {
int *p = (int *)operator new(0);
- free(p); // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not free()}}
+ free(p); // expected-warning{{Memory allocated by 'operator new' should be deallocated by 'delete', not 'free()'}}
}
void testNew3() {
int *p = new int[1];
- free(p); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not free()}}
+ free(p); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'free()'}}
}
void testNew4() {
int *p = new int;
- realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not realloc()}}
+ realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not 'realloc()'}}
}
void testNew5() {
int *p = (int *)operator new(0);
- realloc(p, sizeof(long)); // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not realloc()}}
+ realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'operator new' should be deallocated by 'delete', not 'realloc()'}}
}
void testNew6() {
int *p = new int[1];
- realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not realloc()}}
+ realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'realloc()'}}
}
int *allocInt() {
@@ -106,7 +106,7 @@ void testNew7() {
void testNew8() {
int *p = (int *)operator new(0);
- delete[] p; // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not 'delete[]'}}
+ delete[] p; // expected-warning{{Memory allocated by 'operator new' should be deallocated by 'delete', not 'delete[]'}}
}
int *allocIntArray(unsigned c) {
@@ -120,7 +120,7 @@ void testNew9() {
void testNew10() {
int *p = (int *)operator new[](0);
- delete p; // expected-warning{{Memory allocated by operator new[] should be deallocated by 'delete[]', not 'delete'}}
+ delete p; // expected-warning{{Memory allocated by 'operator new[]' should be deallocated by 'delete[]', not 'delete'}}
}
void testNew11(NSUInteger dataLength) {
@@ -208,7 +208,7 @@ explicit SimpleSmartPointer(T *p = 0) : ptr(p) {}
~SimpleSmartPointer() {
delete ptr;
// expected-warning at -1 {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
- // expected-warning at -2 {{Memory allocated by malloc() should be deallocated by free(), not 'delete'}}
+ // expected-warning at -2 {{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'}}
}
};
diff --git a/clang/test/Analysis/NewDelete-intersections.mm b/clang/test/Analysis/NewDelete-intersections.mm
index 6f81034ee349f..9ac471600e8b9 100644
--- a/clang/test/Analysis/NewDelete-intersections.mm
+++ b/clang/test/Analysis/NewDelete-intersections.mm
@@ -44,7 +44,7 @@ void testMallocFreeNoWarn() {
void testDeleteMalloced() {
int *p1 = (int *)malloc(sizeof(int));
delete p1;
- // mismatch-warning at -1{{Memory allocated by malloc() should be deallocated by free(), not 'delete'}}
+ // mismatch-warning at -1{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'}}
int *p2 = (int *)__builtin_alloca(sizeof(int));
delete p2; // no warn
@@ -59,13 +59,13 @@ void testUseZeroAllocatedMalloced() {
void testFreeOpNew() {
void *p = operator new(0);
free(p);
- // mismatch-warning at -1{{Memory allocated by operator new should be deallocated by 'delete', not free()}}
+ // mismatch-warning at -1{{Memory allocated by 'operator new' should be deallocated by 'delete', not 'free()'}}
}
void testFreeNewExpr() {
int *p = new int;
free(p);
- // mismatch-warning at -1{{Memory allocated by 'new' should be deallocated by 'delete', not free()}}
+ // mismatch-warning at -1{{Memory allocated by 'new' should be deallocated by 'delete', not 'free()'}}
free(p);
}
diff --git a/clang/test/Analysis/free.c b/clang/test/Analysis/free.c
index 50c1efdfb1309..5fd956a4ce110 100644
--- a/clang/test/Analysis/free.c
+++ b/clang/test/Analysis/free.c
@@ -13,21 +13,21 @@ void *alloca(size_t);
void t1 (void) {
int a[] = { 1 };
free(a);
- // expected-warning at -1{{Argument to free() is the address of the local variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the local variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
void t2 (void) {
int a = 1;
free(&a);
- // expected-warning at -1{{Argument to free() is the address of the local variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the local variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
void t3 (void) {
static int a[] = { 1 };
free(a);
- // expected-warning at -1{{Argument to free() is the address of the static variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the static variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
@@ -42,7 +42,7 @@ void t5 (void) {
void t6 (void) {
free((void*)1000);
- // expected-warning at -1{{Argument to free() is a constant address (1000), which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is a constant address (1000), which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object '(void *)1000'}}
}
@@ -58,42 +58,42 @@ void t8 (char **x) {
void t9 (void) {
label:
free(&&label);
- // expected-warning at -1{{Argument to free() is the address of the label 'label', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the label 'label', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'label'}}
}
void t10 (void) {
free((void*)&t10);
- // expected-warning at -1{{Argument to free() is the address of the function 't10', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the function 't10', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 't10'}}
}
void t11 (void) {
char *p = (char*)alloca(2);
- free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void t12 (void) {
char *p = (char*)__builtin_alloca(2);
- free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void t13 (void) {
free(^{return;});
- // expected-warning at -1{{Argument to free() is a block, which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is a block, which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object: block expression}}
}
void t14 (char a) {
free(&a);
- // expected-warning at -1{{Argument to free() is the address of the parameter 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the parameter 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
static int someGlobal[2];
void t15 (void) {
free(someGlobal);
- // expected-warning at -1{{Argument to free() is the address of the global variable 'someGlobal', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the global variable 'someGlobal', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'someGlobal'}}
}
@@ -105,7 +105,7 @@ void t16 (char **x, int offset) {
int *iptr(void);
void t17(void) {
free(iptr); // Oops, forgot to call iptr().
- // expected-warning at -1{{Argument to free() is the address of the function 'iptr', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the function 'iptr', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'iptr'}}
}
diff --git a/clang/test/Analysis/free.cpp b/clang/test/Analysis/free.cpp
index a812a22c47d39..b7f2e49855cf1 100644
--- a/clang/test/Analysis/free.cpp
+++ b/clang/test/Analysis/free.cpp
@@ -17,42 +17,42 @@ extern "C" void *alloca(std::size_t);
void t1a () {
int a[] = { 1 };
free(a);
- // expected-warning at -1{{Argument to free() is the address of the local variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the local variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
void t1b () {
int a[] = { 1 };
std::free(a);
- // expected-warning at -1{{Argument to free() is the address of the local variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the local variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object 'a'}}
}
void t2a () {
int a = 1;
free(&a);
- // expected-warning at -1{{Argument to free() is the address of the local variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the local variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
void t2b () {
int a = 1;
std::free(&a);
- // expected-warning at -1{{Argument to free() is the address of the local variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the local variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object 'a'}}
}
void t3a () {
static int a[] = { 1 };
free(a);
- // expected-warning at -1{{Argument to free() is the address of the static variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the static variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
void t3b () {
static int a[] = { 1 };
std::free(a);
- // expected-warning at -1{{Argument to free() is the address of the static variable 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the static variable 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object 'a'}}
}
@@ -76,13 +76,13 @@ void t5b () {
void t6a () {
free((void*)1000);
- // expected-warning at -1{{Argument to free() is a constant address (1000), which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is a constant address (1000), which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object '(void *)1000'}}
}
void t6b () {
std::free((void*)1000);
- // expected-warning at -1{{Argument to free() is a constant address (1000), which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is a constant address (1000), which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object '(void *)1000'}}
}
@@ -107,95 +107,95 @@ void t8b (char **x) {
void t9a () {
label:
free(&&label);
- // expected-warning at -1{{Argument to free() is the address of the label 'label', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the label 'label', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'label'}}
}
void t9b () {
label:
std::free(&&label);
- // expected-warning at -1{{Argument to free() is the address of the label 'label', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the label 'label', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object 'label'}}
}
void t10a () {
free((void*)&t10a);
- // expected-warning at -1{{Argument to free() is the address of the function 't10a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the function 't10a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 't10a'}}
}
void t10b () {
std::free((void*)&t10b);
- // expected-warning at -1{{Argument to free() is the address of the function 't10b', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the function 't10b', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object 't10b'}}
}
void t11a () {
char *p = (char*)alloca(2);
- free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void t11b () {
char *p = (char*)alloca(2);
- std::free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ std::free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void t12a () {
char *p = (char*)__builtin_alloca(2);
- free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void t12b () {
char *p = (char*)__builtin_alloca(2);
- std::free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ std::free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void t13a () {
free(^{return;});
- // expected-warning at -1{{Argument to free() is a block, which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is a block, which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object: block expression}}
}
void t13b () {
std::free(^{return;});
- // expected-warning at -1{{Argument to free() is a block, which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is a block, which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object: block expression}}
}
void t14a () {
free((void *)+[]{ return; });
- // expected-warning at -1{{Argument to free() is the address of the function '__invoke', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the function '__invoke', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object: lambda-to-function-pointer conversion}}
}
void t14b () {
std::free((void *)+[]{ return; });
- // expected-warning at -1{{Argument to free() is the address of the function '__invoke', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the function '__invoke', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object: lambda-to-function-pointer conversion}}
}
void t15a (char a) {
free(&a);
- // expected-warning at -1{{Argument to free() is the address of the parameter 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the parameter 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'a'}}
}
void t15b (char a) {
std::free(&a);
- // expected-warning at -1{{Argument to free() is the address of the parameter 'a', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the parameter 'a', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object 'a'}}
}
static int someGlobal[2];
void t16a () {
free(someGlobal);
- // expected-warning at -1{{Argument to free() is the address of the global variable 'someGlobal', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the global variable 'someGlobal', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 'someGlobal'}}
}
void t16b () {
std::free(someGlobal);
- // expected-warning at -1{{Argument to free() is the address of the global variable 'someGlobal', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the global variable 'someGlobal', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call std::free on non-heap object 'someGlobal'}}
}
diff --git a/clang/test/Analysis/getline-alloc.c b/clang/test/Analysis/getline-alloc.c
index 5b5c716cb605a..74a40a11b9782 100644
--- a/clang/test/Analysis/getline-alloc.c
+++ b/clang/test/Analysis/getline-alloc.c
@@ -40,7 +40,7 @@ void test_getline_alloca() {
return;
size_t n = 10;
char *buffer = alloca(n);
- getline(&buffer, &n, F1); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ getline(&buffer, &n, F1); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
fclose(F1);
}
@@ -50,7 +50,7 @@ void test_getline_invalid_ptr() {
return;
size_t n = 10;
char *buffer = (char*)test_getline_invalid_ptr;
- getline(&buffer, &n, F1); // expected-warning {{Argument to getline() is the address of the function 'test_getline_invalid_ptr', which is not memory allocated by malloc()}}
+ getline(&buffer, &n, F1); // expected-warning {{Argument to 'getline()' is the address of the function 'test_getline_invalid_ptr', which is not memory allocated by 'malloc()'}}
fclose(F1);
}
@@ -79,7 +79,7 @@ void test_getline_stack() {
if (!F1)
return;
- getline(&ptr, &n, F1); // expected-warning {{Argument to getline() is the address of the local variable 'buffer', which is not memory allocated by malloc()}}
+ getline(&ptr, &n, F1); // expected-warning {{Argument to 'getline()' is the address of the local variable 'buffer', which is not memory allocated by 'malloc()'}}
}
void test_getline_static() {
@@ -91,5 +91,5 @@ void test_getline_static() {
if (!F1)
return;
- getline(&ptr, &n, F1); // expected-warning {{Argument to getline() is the address of the static variable 'buffer', which is not memory allocated by malloc()}}
+ getline(&ptr, &n, F1); // expected-warning {{Argument to 'getline()' is the address of the static variable 'buffer', which is not memory allocated by 'malloc()'}}
}
diff --git a/clang/test/Analysis/kmalloc-linux.c b/clang/test/Analysis/kmalloc-linux.c
index af1af24126b6a..0114c4ef000e2 100644
--- a/clang/test/Analysis/kmalloc-linux.c
+++ b/clang/test/Analysis/kmalloc-linux.c
@@ -133,5 +133,5 @@ void test_kfree_ZERO_SIZE_PTR(void) {
void test_kfree_other_constant_value(void) {
void *ptr = (void *)1;
- kfree(ptr); // expected-warning{{Argument to kfree() is a constant address (1)}}
+ kfree(ptr); // expected-warning{{Argument to 'kfree()' is a constant address (1)}}
}
diff --git a/clang/test/Analysis/malloc-fnptr-plist.c b/clang/test/Analysis/malloc-fnptr-plist.c
index e3482980dfbf6..9e82406459234 100644
--- a/clang/test/Analysis/malloc-fnptr-plist.c
+++ b/clang/test/Analysis/malloc-fnptr-plist.c
@@ -5,7 +5,7 @@ void free(void *);
void (*fnptr)(int);
void foo(void) {
free((void *)fnptr);
- // expected-warning at -1{{Argument to free() is a function pointer}}
+ // expected-warning at -1{{Argument to 'free()' is a function pointer}}
// expected-warning at -2{{attempt to call free on non-heap object '(void *)fnptr'}}
}
diff --git a/clang/test/Analysis/malloc-std-namespace.cpp b/clang/test/Analysis/malloc-std-namespace.cpp
index d4e397bb812aa..21566ad617920 100644
--- a/clang/test/Analysis/malloc-std-namespace.cpp
+++ b/clang/test/Analysis/malloc-std-namespace.cpp
@@ -18,7 +18,7 @@ void no_leak() {
void invalid_free() {
int i;
int *p = &i;
- //expected-note at +2{{Argument to free() is the address of the local variable 'i', which is not memory allocated by malloc()}}
- //expected-warning at +1{{Argument to free() is the address of the local variable 'i', which is not memory allocated by malloc()}}
+ //expected-note at +2{{Argument to 'free()' is the address of the local variable 'i', which is not memory allocated by 'malloc()'}}
+ //expected-warning at +1{{Argument to 'free()' is the address of the local variable 'i', which is not memory allocated by 'malloc()'}}
std::free(p);
}
diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c
index 8ee29aeb324f7..9c7ca43bfbc5a 100644
--- a/clang/test/Analysis/malloc.c
+++ b/clang/test/Analysis/malloc.c
@@ -732,7 +732,7 @@ void mallocCastToFP(void) {
free(p);
}
-// This tests that malloc() buffers are undefined by default
+// This tests that 'malloc()' buffers are undefined by default
char mallocGarbage (void) {
char *buf = malloc(2);
char result = buf[1]; // expected-warning{{undefined}}
@@ -778,17 +778,17 @@ void paramFree(int *p) {
void allocaFree(void) {
int *p = alloca(sizeof(int));
- free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void allocaFreeBuiltin(void) {
int *p = __builtin_alloca(sizeof(int));
- free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
void allocaFreeBuiltinAlign(void) {
int *p = __builtin_alloca_with_align(sizeof(int), 64);
- free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
+ free(p); // expected-warning {{Memory allocated by 'alloca()' should not be deallocated}}
}
@@ -1327,7 +1327,7 @@ void radar10978247_positive(int myValueSize) {
else
return; // expected-warning {{leak}}
}
-// Previously this triggered a false positive because malloc() is known to
+// Previously this triggered a false positive because 'malloc()' is known to
// return uninitialized memory and the binding of 'o' to 'p->n' was not getting
// propertly handled. Now we report a leak.
struct rdar11269741_a_t {
@@ -1656,26 +1656,26 @@ void testOffsetDeallocate(int *memoryBlock) {
void testOffsetOfRegionFreed(void) {
__int64_t * array = malloc(sizeof(__int64_t)*2);
array += 1;
- free(&array[0]); // expected-warning{{Argument to free() is offset by 8 bytes from the start of memory allocated by malloc()}}
+ free(&array[0]); // expected-warning{{Argument to 'free()' is offset by 8 bytes from the start of memory allocated by 'malloc()'}}
}
void testOffsetOfRegionFreed2(void) {
__int64_t *p = malloc(sizeof(__int64_t)*2);
p += 1;
- free(p); // expected-warning{{Argument to free() is offset by 8 bytes from the start of memory allocated by malloc()}}
+ free(p); // expected-warning{{Argument to 'free()' is offset by 8 bytes from the start of memory allocated by 'malloc()'}}
}
void testOffsetOfRegionFreed3(void) {
char *r = malloc(sizeof(char));
r = r - 10;
- free(r); // expected-warning {{Argument to free() is offset by -10 bytes from the start of memory allocated by malloc()}}
+ free(r); // expected-warning {{Argument to 'free()' is offset by -10 bytes from the start of memory allocated by 'malloc()'}}
}
void testOffsetOfRegionFreedAfterFunctionCall(void) {
int *p = malloc(sizeof(int)*2);
p += 1;
myfoo(p);
- free(p); // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}}
+ free(p); // expected-warning{{Argument to 'free()' is offset by 4 bytes from the start of memory allocated by 'malloc()'}}
}
void testFixManipulatedPointerBeforeFree(void) {
@@ -1695,7 +1695,7 @@ void freeOffsetPointerPassedToFunction(void) {
p[1] = 0;
p += 1;
myfooint(*p); // not passing the pointer, only a value pointed by pointer
- free(p); // expected-warning {{Argument to free() is offset by 8 bytes from the start of memory allocated by malloc()}}
+ free(p); // expected-warning {{Argument to 'free()' is offset by 8 bytes from the start of memory allocated by 'malloc()'}}
}
int arbitraryInt(void);
@@ -1709,13 +1709,13 @@ void testFreeNonMallocPointerWithNoOffset(void) {
char c;
char *r = &c;
r = r + 10;
- free(r-10); // expected-warning {{Argument to free() is the address of the local variable 'c', which is not memory allocated by malloc()}}
+ free(r-10); // expected-warning {{Argument to 'free()' is the address of the local variable 'c', which is not memory allocated by 'malloc()'}}
}
void testFreeNonMallocPointerWithOffset(void) {
char c;
char *r = &c;
- free(r+1); // expected-warning {{Argument to free() is the address of the local variable 'c', which is not memory allocated by malloc()}}
+ free(r+1); // expected-warning {{Argument to 'free()' is the address of the local variable 'c', which is not memory allocated by 'malloc()'}}
}
void testOffsetZeroDoubleFree(void) {
@@ -1735,14 +1735,14 @@ void testOffsetPassedToStrlenThenFree(void) {
char * string = malloc(sizeof(char)*10);
string += 1;
int length = strlen(string);
- free(string); // expected-warning {{Argument to free() is offset by 1 byte from the start of memory allocated by malloc()}}
+ free(string); // expected-warning {{Argument to 'free()' is offset by 1 byte from the start of memory allocated by 'malloc()'}}
}
void testOffsetPassedAsConst(void) {
char * string = malloc(sizeof(char)*10);
string += 1;
passConstPtr(string);
- free(string); // expected-warning {{Argument to free() is offset by 1 byte from the start of memory allocated by malloc()}}
+ free(string); // expected-warning {{Argument to 'free()' is offset by 1 byte from the start of memory allocated by 'malloc()'}}
}
char **_vectorSegments;
@@ -1842,12 +1842,12 @@ int testNoCheckerDataPropogationFromLogicalOpOperandToOpResult(void) {
void (*fnptr)(int);
void freeIndirectFunctionPtr(void) {
void *p = (void *)fnptr;
- free(p); // expected-warning {{Argument to free() is a function pointer}}
+ free(p); // expected-warning {{Argument to 'free()' is a function pointer}}
}
void freeFunctionPtr(void) {
free((void *)fnptr);
- // expected-warning at -1{{Argument to free() is a function pointer}}
+ // expected-warning at -1{{Argument to 'free()' is a function pointer}}
// expected-warning at -2{{attempt to call free on non-heap object '(void *)fnptr'}}
}
@@ -1905,8 +1905,8 @@ enum { BUFSIZE = 256 };
void MEM34_C(void) {
char buf[BUFSIZE];
char *p = (char *)realloc(buf, 2 * BUFSIZE);
- // expected-warning at -1{{Argument to realloc() is the address of the local \
-variable 'buf', which is not memory allocated by malloc() [unix.Malloc]}}
+ // expected-warning at -1{{Argument to 'realloc()' is the address of the local \
+variable 'buf', which is not memory allocated by 'malloc()' [unix.Malloc]}}
if (p == NULL) {
/* Handle error */
}
diff --git a/clang/test/Analysis/malloc.mm b/clang/test/Analysis/malloc.mm
index 94a46d731090b..5b816a1524aec 100644
--- a/clang/test/Analysis/malloc.mm
+++ b/clang/test/Analysis/malloc.mm
@@ -89,7 +89,7 @@ void testNSStringFreeWhenDoneNO2(NSUInteger dataLength) {
void testOffsetFree() {
int *p = (int *)malloc(sizeof(int));
- NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to +dataWithBytesNoCopy:length:freeWhenDone: is offset by 4 bytes from the start of memory allocated by malloc()}}
+ NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to +dataWithBytesNoCopy:length:freeWhenDone: is offset by 4 bytes from the start of memory allocated by 'malloc()'}}
}
void testRelinquished1() {
diff --git a/clang/test/Analysis/plist-macros.cpp b/clang/test/Analysis/plist-macros.cpp
index 94f8e514c8b39..d7b3c7cc1c86f 100644
--- a/clang/test/Analysis/plist-macros.cpp
+++ b/clang/test/Analysis/plist-macros.cpp
@@ -13,7 +13,7 @@ void noteOnMacro(int y) {
mallocmemory
y++;
y++;
- delete x; // expected-warning {{Memory allocated by malloc() should be deallocated by free(), not 'delete'}}
+ delete x; // expected-warning {{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'delete'}}
}
void macroIsFirstInFunction(int y) {
diff --git a/clang/test/Analysis/weak-functions.c b/clang/test/Analysis/weak-functions.c
index 26cbfb3523a92..5bdb411fcbf4f 100644
--- a/clang/test/Analysis/weak-functions.c
+++ b/clang/test/Analysis/weak-functions.c
@@ -72,7 +72,7 @@ void free(void *) __attribute__((weak_import));
void t10 (void) {
free((void*)&t10);
- // expected-warning at -1{{Argument to free() is the address of the function 't10', which is not memory allocated by malloc()}}
+ // expected-warning at -1{{Argument to 'free()' is the address of the function 't10', which is not memory allocated by 'malloc()'}}
// expected-warning at -2{{attempt to call free on non-heap object 't10'}}
}
>From d3e06bbbb1b0b2f0752a1ac6328e7027a9476ac8 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Mon, 15 Jul 2024 00:57:21 +0300
Subject: [PATCH 6/6] csa/tests: add test suite for custom allocation classes
---
.../MismatchedDeallocator-checker-test.mm | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
index bbf24837f9da8..ef8b24ba8de32 100644
--- a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
+++ b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
@@ -14,6 +14,19 @@
void free(void *);
void __attribute((ownership_takes(malloc, 1))) my_free(void *);
+void __attribute((ownership_returns(malloc1))) *my_malloc1(size_t);
+void __attribute((ownership_takes(malloc1, 1))) my_free1(void *);
+
+void __attribute((ownership_returns(malloc2))) *my_malloc2(size_t);
+
+// The order of these declarations are important to verify that analisys still works even
+// if there are less specific declarations of the same functions
+void __attribute((ownership_returns(malloc3))) *my_malloc3(size_t);
+void *my_malloc3(size_t);
+
+void *my_malloc4(size_t);
+void __attribute((ownership_returns(malloc4))) *my_malloc4(size_t);
+
//---------------------------------------------------------------
// Test if an allocation function matches deallocation function
//---------------------------------------------------------------
@@ -60,6 +73,41 @@ void testMalloc8() {
operator delete[](p); // expected-warning{{Memory allocated by 'malloc()' should be deallocated by 'free()', not 'operator delete[]'}}
}
+void testMalloc9() {
+ int *p = (int *)my_malloc(sizeof(int));
+ my_free(p); // no warning
+}
+
+void testMalloc10() {
+ int *p = (int *)my_malloc1(sizeof(int));
+ my_free1(p); // no warning
+}
+
+void testMalloc11() {
+ int *p = (int *)my_malloc1(sizeof(int));
+ my_free(p); // expected-warning{{Memory allocated by 'my_malloc1()' should be deallocated by function that takes ownership of 'malloc1', not 'my_free()', which takes ownership of 'malloc'}}
+}
+
+void testMalloc12() {
+ int *p = (int *)my_malloc2(sizeof(int));
+ my_free1(p); // expected-warning{{Memory allocated by 'my_malloc2()' should be deallocated by function that takes ownership of 'malloc2', not 'my_free1()', which takes ownership of 'malloc1'}}
+}
+
+void testMalloc13() {
+ int *p = (int *)my_malloc1(sizeof(int));
+ free(p); // expected-warning{{Memory allocated by 'my_malloc1()' should be deallocated by function that takes ownership of 'malloc1', not 'free()'}}
+}
+
+void testMalloc14() {
+ int *p = (int *)my_malloc3(sizeof(int));
+ free(p); // expected-warning{{Memory allocated by 'my_malloc3()' should be deallocated by function that takes ownership of 'malloc3', not 'free()'}}
+}
+
+void testMalloc15() {
+ int *p = (int *)my_malloc4(sizeof(int));
+ free(p); // expected-warning{{Memory allocated by 'my_malloc4()' should be deallocated by function that takes ownership of 'malloc4', not 'free()'}}
+}
+
void testAlloca() {
int *p = (int *)__builtin_alloca(sizeof(int));
delete p; // expected-warning{{Memory allocated by 'alloca()' should not be deallocated}}
More information about the cfe-commits
mailing list