[clang] [analyzer] Add MemSpace trait to program state (PR #123003)
Michael Flanders via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 15 07:13:36 PST 2025
https://github.com/Flandini updated https://github.com/llvm/llvm-project/pull/123003
>From 7e0758d2ead53bd4288989b8b2eda218cd6dc34a Mon Sep 17 00:00:00 2001
From: Michael Flanders <flanders.michaelk at gmail.com>
Date: Mon, 13 Jan 2025 12:34:50 -0600
Subject: [PATCH 1/2] [analyzer] Add MemSpace trait to program state
This trait associates MemSpaceRegions with MemRegions to allow refining or
changing information known about memory regions after they are created, since
they are immutable. This commit is an intermediate step towards moving
MemSpaceRegion out of the MemRegion class hierarchy and moving all notion of
MemSpaceRegion to the trait. The intermediate step is that for now, only
MemRegions with UnknownSpaceRegion are mapped in the trait and checked in
checkers/core.
---
.../Core/PathSensitive/MemSpaces.h | 47 ++++++++++++++
.../Checkers/ArrayBoundCheckerV2.cpp | 1 +
.../Checkers/MacOSXAPIChecker.cpp | 10 +--
.../StaticAnalyzer/Checkers/MallocChecker.cpp | 32 +++++++---
.../StaticAnalyzer/Checkers/MoveChecker.cpp | 41 ++++++------
.../Checkers/PutenvStackArrayChecker.cpp | 10 ++-
.../RetainCountDiagnostics.cpp | 12 +++-
.../Checkers/StackAddrEscapeChecker.cpp | 26 +++++---
.../Checkers/UnixAPIChecker.cpp | 3 +-
.../Core/BugReporterVisitors.cpp | 12 +++-
clang/lib/StaticAnalyzer/Core/CMakeLists.txt | 1 +
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 10 +++
clang/lib/StaticAnalyzer/Core/MemSpaces.cpp | 62 +++++++++++++++++++
13 files changed, 221 insertions(+), 46 deletions(-)
create mode 100644 clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h
create mode 100644 clang/lib/StaticAnalyzer/Core/MemSpaces.cpp
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h
new file mode 100644
index 00000000000000..178a6b60c1cb1a
--- /dev/null
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h
@@ -0,0 +1,47 @@
+//===-- MemSpaces.h -----------------------------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// TODO
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_CHECKERS_MEMSPACES_H
+#define LLVM_CLANG_STATICANALYZER_CHECKERS_MEMSPACES_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+
+namespace clang {
+namespace ento {
+
+class MemRegion;
+class MemSpaceRegion;
+
+namespace memspace {
+
+[[nodiscard]] ProgramStateRef setMemSpaceTrait(ProgramStateRef State,
+ const MemRegion *MR,
+ const MemSpaceRegion *MS);
+
+[[nodiscard]] const MemSpaceRegion *getMemSpaceTrait(ProgramStateRef State,
+ const MemRegion *MR);
+
+[[nodiscard]] bool hasMemSpaceTrait(ProgramStateRef State, const MemRegion *MR);
+
+template <typename FirstT, typename... RestT>
+[[nodiscard]] bool isMemSpaceOrTrait(ProgramStateRef State,
+ const MemRegion *MR) {
+ return isa<FirstT, RestT...>(MR->getMemorySpace()) ||
+ isa_and_nonnull<FirstT, RestT...>(getMemSpaceTrait(State, MR));
+}
+
+} // namespace memspace
+} // namespace ento
+} // namespace clang
+
+#endif // LLVM_CLANG_STATICANALYZER_CHECKERS_MEMSPACES_H
diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 6422933c8828a9..e97be53fee4c7f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -22,6 +22,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FormatVariadic.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index 754b167642961c..858b6a37551f5d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -22,6 +22,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
@@ -75,10 +76,11 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
if (!R)
return;
+ ProgramStateRef State = C.getState();
+
// Global variables are fine.
const MemRegion *RB = R->getBaseRegion();
- const MemSpaceRegion *RS = RB->getMemorySpace();
- if (isa<GlobalsSpaceRegion>(RS))
+ if (memspace::isMemSpaceOrTrait<GlobalsSpaceRegion>(State, RB))
return;
// Handle _dispatch_once. In some versions of the OS X SDK we have the case
@@ -117,9 +119,9 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
if (IVR != R)
os << " memory within";
os << " the instance variable '" << IVR->getDecl()->getName() << '\'';
- } else if (isa<HeapSpaceRegion>(RS)) {
+ } else if (memspace::isMemSpaceOrTrait<HeapSpaceRegion>(State, RB)) {
os << " heap-allocated memory";
- } else if (isa<UnknownSpaceRegion>(RS)) {
+ } else if (isa<UnknownSpaceRegion>(RB->getMemorySpace())) {
// Presence of an IVar superregion has priority over this branch, because
// ObjC objects are on the heap even if the core doesn't realize this.
// Presence of a block variable base region has priority over this branch,
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 4166cf14391e2d..76fee2f1322e17 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -72,6 +72,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
@@ -784,7 +785,8 @@ class MallocChecker
bool IsALeakCheck = false) const;
///@}
static bool SummarizeValue(raw_ostream &os, SVal V);
- static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
+ static bool SummarizeRegion(ProgramStateRef State, raw_ostream &os,
+ const MemRegion *MR);
void HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, SourceRange Range,
const Expr *DeallocExpr,
@@ -2206,13 +2208,21 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
// Parameters, locals, statics, globals, and memory returned by
// __builtin_alloca() shouldn't be freed.
- if (!isa<UnknownSpaceRegion, HeapSpaceRegion>(MS)) {
+ // Should skip this check if:
+ // - The region is in the heap
+ // - The region has unknown memspace and no trait for further clarification
+ // (i.e., just unknown)
+ // - The region has unknown memspace and a heap memspace trait
+ const MemSpaceRegion *MSTrait = memspace::getMemSpaceTrait(State, R);
+ bool HasHeapOrUnknownTrait = !MSTrait || isa<HeapSpaceRegion>(MSTrait);
+ if (!(isa<HeapSpaceRegion>(MS) ||
+ (isa<UnknownSpaceRegion>(MS) && HasHeapOrUnknownTrait))) {
// Regions returned by malloc() are represented by SymbolicRegion objects
// within HeapSpaceRegion. Of course, free() can work on memory allocated
// outside the current function, so UnknownSpaceRegion is also a
// possibility here.
- if (isa<AllocaRegion>(R))
+ if (isa<AllocaRegion>(R) || isa_and_nonnull<AllocaRegion>(MSTrait))
HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
else
HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
@@ -2384,7 +2394,7 @@ bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
return true;
}
-bool MallocChecker::SummarizeRegion(raw_ostream &os,
+bool MallocChecker::SummarizeRegion(ProgramStateRef State, raw_ostream &os,
const MemRegion *MR) {
switch (MR->getKind()) {
case MemRegion::FunctionCodeRegionKind: {
@@ -2404,8 +2414,10 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os,
return true;
default: {
const MemSpaceRegion *MS = MR->getMemorySpace();
+ const MemSpaceRegion *MSTrait = memspace::getMemSpaceTrait(State, MR);
- if (isa<StackLocalsSpaceRegion>(MS)) {
+ if (isa<StackLocalsSpaceRegion>(MS) ||
+ isa_and_nonnull<StackLocalsSpaceRegion>(MSTrait)) {
const VarRegion *VR = dyn_cast<VarRegion>(MR);
const VarDecl *VD;
if (VR)
@@ -2420,7 +2432,8 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os,
return true;
}
- if (isa<StackArgumentsSpaceRegion>(MS)) {
+ if (isa<StackArgumentsSpaceRegion>(MS) ||
+ isa_and_nonnull<StackArgumentsSpaceRegion>(MSTrait)) {
const VarRegion *VR = dyn_cast<VarRegion>(MR);
const VarDecl *VD;
if (VR)
@@ -2435,7 +2448,8 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os,
return true;
}
- if (isa<GlobalsSpaceRegion>(MS)) {
+ if (isa<GlobalsSpaceRegion>(MS) ||
+ isa_and_nonnull<GlobalsSpaceRegion>(MSTrait)) {
const VarRegion *VR = dyn_cast<VarRegion>(MR);
const VarDecl *VD;
if (VR)
@@ -2489,8 +2503,8 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal,
os << "deallocator";
os << " is ";
- bool Summarized = MR ? SummarizeRegion(os, MR)
- : SummarizeValue(os, ArgVal);
+ bool Summarized =
+ MR ? SummarizeRegion(C.getState(), os, MR) : SummarizeValue(os, ArgVal);
if (Summarized)
os << ", which is not memory allocated by ";
else
diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index 52416e21399147..d4ff8186ea69c9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -22,6 +22,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "llvm/ADT/StringSet.h"
using namespace clang;
@@ -145,12 +146,14 @@ class MoveChecker
// Obtains ObjectKind of an object. Because class declaration cannot always
// be easily obtained from the memory region, it is supplied separately.
- ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const;
+ ObjectKind classifyObject(ProgramStateRef State, const MemRegion *MR,
+ const CXXRecordDecl *RD) const;
// Classifies the object and dumps a user-friendly description string to
// the stream.
- void explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
- const CXXRecordDecl *RD, MisuseKind MK) const;
+ void explainObject(ProgramStateRef State, llvm::raw_ostream &OS,
+ const MemRegion *MR, const CXXRecordDecl *RD,
+ MisuseKind MK) const;
bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const;
@@ -299,12 +302,12 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
- ObjectKind OK = Chk.classifyObject(Region, RD);
+ ObjectKind OK = Chk.classifyObject(State, Region, RD);
switch (OK.StdKind) {
case SK_SmartPtr:
if (MK == MK_Dereference) {
OS << "Smart pointer";
- Chk.explainObject(OS, Region, RD, MK);
+ Chk.explainObject(State, OS, Region, RD, MK);
OS << " is reset to null when moved from";
break;
}
@@ -315,12 +318,12 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
case SK_NonStd:
case SK_Safe:
OS << "Object";
- Chk.explainObject(OS, Region, RD, MK);
+ Chk.explainObject(State, OS, Region, RD, MK);
OS << " is moved";
break;
case SK_Unsafe:
OS << "Object";
- Chk.explainObject(OS, Region, RD, MK);
+ Chk.explainObject(State, OS, Region, RD, MK);
OS << " is left in a valid but unspecified state after move";
break;
}
@@ -353,7 +356,7 @@ void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region,
CheckerContext &C) const {
assert(!C.isDifferent() && "No transitions should have been made by now");
const RegionState *RS = State->get<TrackedRegionMap>(Region);
- ObjectKind OK = classifyObject(Region, RD);
+ ObjectKind OK = classifyObject(State, Region, RD);
// Just in case: if it's not a smart pointer but it does have operator *,
// we shouldn't call the bug a dereference.
@@ -406,24 +409,25 @@ ExplodedNode *MoveChecker::tryToReportBug(const MemRegion *Region,
// Creating the error message.
llvm::SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
+ ProgramStateRef ErrorNodeState = N->getState();
switch(MK) {
case MK_FunCall:
OS << "Method called on moved-from object";
- explainObject(OS, Region, RD, MK);
+ explainObject(ErrorNodeState, OS, Region, RD, MK);
break;
case MK_Copy:
OS << "Moved-from object";
- explainObject(OS, Region, RD, MK);
+ explainObject(ErrorNodeState, OS, Region, RD, MK);
OS << " is copied";
break;
case MK_Move:
OS << "Moved-from object";
- explainObject(OS, Region, RD, MK);
+ explainObject(ErrorNodeState, OS, Region, RD, MK);
OS << " is moved";
break;
case MK_Dereference:
OS << "Dereference of null smart pointer";
- explainObject(OS, Region, RD, MK);
+ explainObject(ErrorNodeState, OS, Region, RD, MK);
break;
}
@@ -482,7 +486,7 @@ void MoveChecker::checkPostCall(const CallEvent &Call,
return;
const CXXRecordDecl *RD = MethodDecl->getParent();
- ObjectKind OK = classifyObject(ArgRegion, RD);
+ ObjectKind OK = classifyObject(State, ArgRegion, RD);
if (shouldBeTracked(OK)) {
// Mark object as moved-from.
State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
@@ -549,7 +553,7 @@ bool MoveChecker::belongsTo(const CXXRecordDecl *RD,
}
MoveChecker::ObjectKind
-MoveChecker::classifyObject(const MemRegion *MR,
+MoveChecker::classifyObject(ProgramStateRef State, const MemRegion *MR,
const CXXRecordDecl *RD) const {
// Local variables and local rvalue references are classified as "Local".
// For the purposes of this checker, we classify move-safe STL types
@@ -557,7 +561,7 @@ MoveChecker::classifyObject(const MemRegion *MR,
MR = unwrapRValueReferenceIndirection(MR);
bool IsLocal =
isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) &&
- isa<StackSpaceRegion>(MR->getMemorySpace());
+ memspace::isMemSpaceOrTrait<StackSpaceRegion>(State, MR);
if (!RD || !RD->getDeclContext()->isStdNamespace())
return { IsLocal, SK_NonStd };
@@ -571,8 +575,9 @@ MoveChecker::classifyObject(const MemRegion *MR,
return { IsLocal, SK_Unsafe };
}
-void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
- const CXXRecordDecl *RD, MisuseKind MK) const {
+void MoveChecker::explainObject(ProgramStateRef State, llvm::raw_ostream &OS,
+ const MemRegion *MR, const CXXRecordDecl *RD,
+ MisuseKind MK) const {
// We may need a leading space every time we actually explain anything,
// and we never know if we are to explain anything until we try.
if (const auto DR =
@@ -581,7 +586,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
OS << " '" << RegionDecl->getDeclName() << "'";
}
- ObjectKind OK = classifyObject(MR, RD);
+ ObjectKind OK = classifyObject(State, MR, RD);
switch (OK.StdKind) {
case SK_NonStd:
case SK_Safe:
diff --git a/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp
index bf81d57bf82fd3..4449cb4ae68eb9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp
@@ -21,6 +21,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
using namespace clang;
using namespace ento;
@@ -45,10 +46,15 @@ void PutenvStackArrayChecker::checkPostCall(const CallEvent &Call,
SVal ArgV = Call.getArgSVal(0);
const Expr *ArgExpr = Call.getArgExpr(0);
- const auto *SSR =
- dyn_cast<StackSpaceRegion>(ArgV.getAsRegion()->getMemorySpace());
+ const MemRegion *MR = ArgV.getAsRegion();
+
+ const auto *SSR = dyn_cast<StackSpaceRegion>(MR->getMemorySpace());
+ if (!SSR)
+ SSR = dyn_cast_if_present<StackSpaceRegion>(
+ memspace::getMemSpaceTrait(C.getState(), MR));
if (!SSR)
return;
+
const auto *StackFrameFuncD =
dyn_cast_or_null<FunctionDecl>(SSR->getStackFrame()->getDecl());
if (StackFrameFuncD && StackFrameFuncD->isMain())
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index 456132ef0a0a22..ecc7563ceb4bba 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -13,6 +13,7 @@
#include "RetainCountDiagnostics.h"
#include "RetainCountChecker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include <optional>
@@ -690,9 +691,14 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
const MemRegion *R = FB.getRegion();
// Do not show local variables belonging to a function other than
// where the error is reported.
- if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
- if (MR->getStackFrame() == LeakContext->getStackFrame())
- FirstBinding = R;
+ const StackSpaceRegion *MR =
+ dyn_cast<StackSpaceRegion>(R->getMemorySpace());
+ if (!MR)
+ MR = dyn_cast_if_present<StackSpaceRegion>(
+ memspace::getMemSpaceTrait(St, R));
+
+ if (MR && MR->getStackFrame() == LeakContext->getStackFrame())
+ FirstBinding = R;
}
// AllocationNode is the last node in which the symbol was tracked.
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index f4de3b500499c4..4d8c8eea14ea63 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
@@ -61,7 +62,7 @@ class StackAddrEscapeChecker
ASTContext &Ctx);
static SmallVector<const MemRegion *, 4>
getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
- static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
+ static bool isNotInCurrentFrame(const MemSpaceRegion *MS, CheckerContext &C);
};
} // namespace
@@ -117,9 +118,9 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
return range;
}
-bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
+bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemSpaceRegion *MS,
CheckerContext &C) {
- const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
+ const StackSpaceRegion *S = cast<StackSpaceRegion>(MS);
return S->getStackFrame() != C.getStackFrame();
}
@@ -138,10 +139,11 @@ SmallVector<const MemRegion *, 4>
StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
CheckerContext &C) {
SmallVector<const MemRegion *, 4> Regions;
+ ProgramStateRef State = C.getState();
for (auto Var : B.referenced_vars()) {
- SVal Val = C.getState()->getSVal(Var.getCapturedRegion());
+ SVal Val = State->getSVal(Var.getCapturedRegion());
const MemRegion *Region = Val.getAsRegion();
- if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
+ if (Region && memspace::isMemSpaceOrTrait<StackSpaceRegion>(State, Region))
Regions.push_back(Region);
}
return Regions;
@@ -212,7 +214,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
void StackAddrEscapeChecker::checkReturnedBlockCaptures(
const BlockDataRegion &B, CheckerContext &C) const {
for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
- if (isNotInCurrentFrame(Region, C))
+ if (isNotInCurrentFrame(Region->getMemorySpace(), C))
continue;
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
@@ -265,7 +267,14 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
checkReturnedBlockCaptures(*B, C);
- if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C))
+ if (isa<UnknownSpaceRegion>(R)) {
+ const MemSpaceRegion *MS = memspace::getMemSpaceTrait(C.getState(), R);
+ if (!isa_and_nonnull<StackSpaceRegion>(MS) || isNotInCurrentFrame(MS, C))
+ return;
+ }
+
+ const MemSpaceRegion *MS = R->getMemorySpace();
+ if (!isa<StackSpaceRegion>(MS) || isNotInCurrentFrame(MS, C))
return;
// Returning a record by value is fine. (In this case, the returned
@@ -468,7 +477,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
if (!isa_and_nonnull<GlobalsSpaceRegion>(
getStackOrGlobalSpaceRegion(Region)))
return true;
- if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx))
+ if (VR && VR->hasStackStorage() &&
+ !isNotInCurrentFrame(VR->getMemorySpace(), Ctx))
V.emplace_back(Region, VR);
return true;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index da2d16ca9b5dd7..1c6c5fa14bceec 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -21,6 +21,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
@@ -411,7 +412,7 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
// because that's likely to be bad news.
ProgramStateRef state = C.getState();
const MemRegion *R = Call.getArgSVal(0).getAsRegion();
- if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
+ if (!R || !memspace::isMemSpaceOrTrait<StackSpaceRegion>(state, R))
return;
ExplodedNode *N = C.generateErrorNode(state);
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index a9b4dbb39b5bd6..e708bd8ec2f72d 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -40,6 +40,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
@@ -1193,7 +1194,16 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
return false;
const MemSpaceRegion *VarSpace = VR->getMemorySpace();
- const auto *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace);
+ const StackSpaceRegion *FrameSpace;
+
+ if (isa<UnknownSpaceRegion>(VarSpace)) {
+ ProgramStateRef State = N->getState();
+ const MemSpaceRegion *MemSpace = memspace::getMemSpaceTrait(State, VR);
+ FrameSpace = dyn_cast_if_present<StackSpaceRegion>(MemSpace);
+ } else {
+ FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace);
+ }
+
if (!FrameSpace) {
// If we ever directly evaluate global DeclStmts, this assertion will be
// invalid, but this still seems preferable to silently accepting an
diff --git a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
index fb9394a519eb71..993d37bb052e9b 100644
--- a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -36,6 +36,7 @@ add_clang_library(clangStaticAnalyzerCore
LoopUnrolling.cpp
LoopWidening.cpp
MemRegion.cpp
+ MemSpaces.cpp
PlistDiagnostics.cpp
ProgramState.cpp
RangeConstraintManager.cpp
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 70e95c2c644c09..f22d4e7bf9dbd0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -53,6 +53,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
@@ -3516,6 +3517,15 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(
continue;
}
+ // Case (2) continued.
+ if (isa<UnknownSpaceRegion>(MR)) {
+ const MemSpaceRegion *MS = memspace::getMemSpaceTrait(State, MR);
+ if (!isa<StackSpaceRegion, StaticGlobalSpaceRegion>(MS)) {
+ Escaped.push_back(LocAndVal.second);
+ continue;
+ }
+ }
+
// Case (3).
if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion()))
if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame())
diff --git a/clang/lib/StaticAnalyzer/Core/MemSpaces.cpp b/clang/lib/StaticAnalyzer/Core/MemSpaces.cpp
new file mode 100644
index 00000000000000..909d11d6456758
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Core/MemSpaces.cpp
@@ -0,0 +1,62 @@
+//===-- MemSpaces.cpp ---------------------------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// TODO
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemSpaces.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include <cassert>
+
+REGISTER_MAP_WITH_PROGRAMSTATE(MemSpacesMap, const clang::ento::MemRegion *,
+ const clang::ento::MemSpaceRegion *)
+
+namespace clang {
+namespace ento {
+namespace memspace {
+
+ProgramStateRef setMemSpaceTrait(ProgramStateRef State, const MemRegion *MR,
+ const MemSpaceRegion *MS) {
+ // for now, this should only be called to update the trait for mem regions
+ // that have an unknown mem spaces since we assume everywhere else that the
+ // memspace trait is set only for unknown mem spaces (setting this info
+ // otherwise would go unused).
+ assert(isa<UnknownSpaceRegion>(MR->getMemorySpace()));
+
+ // Shouldn't use the memspace trait to associate UnknownSpaceRegion with an
+ // already UnknownSpaceRegion
+ assert(!isa<UnknownSpaceRegion>(MS));
+
+ ProgramStateRef NewState = State->set<MemSpacesMap>(MR, MS);
+ return NewState;
+}
+
+bool hasMemSpaceTrait(ProgramStateRef State, const MemRegion *MR) {
+ if (!isa<UnknownSpaceRegion>(MR->getMemorySpace()))
+ return false;
+
+ const MemSpaceRegion *const *Result = State->get<MemSpacesMap>(MR);
+ return Result;
+}
+
+const MemSpaceRegion *getMemSpaceTrait(ProgramStateRef State,
+ const MemRegion *MR) {
+ if (!isa<UnknownSpaceRegion>(MR->getMemorySpace()))
+ return nullptr;
+
+ const MemSpaceRegion *const *Result = State->get<MemSpacesMap>(MR);
+ if (!Result)
+ return nullptr;
+ return *Result;
+}
+
+} // namespace memspace
+} // namespace ento
+} // namespace clang
>From 94c0e4da693faeacb108e3dc239ed56d99242b3c Mon Sep 17 00:00:00 2001
From: Michael Flanders <flanders.michaelk at gmail.com>
Date: Wed, 15 Jan 2025 09:13:10 -0600
Subject: [PATCH 2/2] Uppercase start and s/mem/memory/ in long comment
---
clang/lib/StaticAnalyzer/Core/MemSpaces.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/MemSpaces.cpp b/clang/lib/StaticAnalyzer/Core/MemSpaces.cpp
index 909d11d6456758..886359fdbfab3d 100644
--- a/clang/lib/StaticAnalyzer/Core/MemSpaces.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemSpaces.cpp
@@ -24,13 +24,13 @@ namespace memspace {
ProgramStateRef setMemSpaceTrait(ProgramStateRef State, const MemRegion *MR,
const MemSpaceRegion *MS) {
- // for now, this should only be called to update the trait for mem regions
- // that have an unknown mem spaces since we assume everywhere else that the
- // memspace trait is set only for unknown mem spaces (setting this info
+ // For now, this should only be called to update the trait for memory regions
+ // that have an unknown memory spaces since we assume everywhere else that the
+ // memory space trait is set only for unknown memory spaces (setting this info
// otherwise would go unused).
assert(isa<UnknownSpaceRegion>(MR->getMemorySpace()));
- // Shouldn't use the memspace trait to associate UnknownSpaceRegion with an
+ // Shouldn't use the memory space trait to associate UnknownSpaceRegion with an
// already UnknownSpaceRegion
assert(!isa<UnknownSpaceRegion>(MS));
More information about the cfe-commits
mailing list