[clang] [clang][analyzer] Preserve symbolic-offset regions across pointer casts (PR #221213)
Radovan Božić via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 4 05:28:12 PDT 2026
https://github.com/bozicrHT created https://github.com/llvm/llvm-project/pull/221213
`castRegion()` returns `nullopt` for `ElementRegion`s with symbolic indices, causing pointer casts to become `UnknownVal` and lose nullness information. Preserve the original region as a zero-offset view of the destination type, while stripping redundant cast views.
Update tests for symbolic pointer casts.
Fixes #220281
>From 9238bc2b15c782fc511a7a292572cafa63b16ca0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <radovan.bozic at htecgroup.com>
Date: Fri, 4 Sep 2026 14:19:17 +0200
Subject: [PATCH] [clang][analyzer] Preserve symbolic-offset regions across
pointer casts
When an ElementRegion has a symbolic index, `castRegion()` cannot
compute a concrete byte offset and returns `nullopt`. Preserve the
original region as a zero-offset view of the destination type and strip
redundant cast views.
---
clang/lib/StaticAnalyzer/Core/Store.cpp | 11 ++++--
clang/test/Analysis/pr22954.c | 5 ++-
clang/test/Analysis/symbolic-pointer-cast.c | 42 +++++++++++++++++++++
3 files changed, 52 insertions(+), 6 deletions(-)
create mode 100644 clang/test/Analysis/symbolic-pointer-cast.c
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index 3b56dde1b56d6..0a91b452ee11d 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -175,10 +175,13 @@ std::optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
const RegionRawOffset &rawOff = elementR->getAsArrayOffset();
const MemRegion *baseR = rawOff.getRegion();
- // If we cannot compute a raw offset, throw up our hands and return
- // a NULL MemRegion*.
- if (!baseR)
- return std::nullopt;
+ if (!baseR) {
+ const MemRegion *uncastedR = elementR->StripCasts(false);
+ if (IsSameRegionType(uncastedR, CanonPointeeTy))
+ return uncastedR;
+
+ return MakeElementRegion(cast<SubRegion>(uncastedR), PointeeTy);
+ }
CharUnits off = rawOff.getOffset();
diff --git a/clang/test/Analysis/pr22954.c b/clang/test/Analysis/pr22954.c
index b3910da6c70ab..1a14d83ca2ae9 100644
--- a/clang/test/Analysis/pr22954.c
+++ b/clang/test/Analysis/pr22954.c
@@ -615,7 +615,8 @@ int f29(int i, int j, int k, int l, int m) {
l29->s1[m] = 2;
char input[] = {'a', 'b', 'c', 'd'};
memcpy(l29->s1, input, 4);
- clang_analyzer_eval(m29[0].s3[0] == 1); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(m29[0].s3[0] == 1); // expected-warning{{UNKNOWN}}\
+ expected-warning{{Potential leak of memory pointed to by field 's4'}}
clang_analyzer_eval(m29[0].s3[1] == 1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(m29[0].s3[2] == 1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(m29[0].s3[3] == 1); // expected-warning{{UNKNOWN}}
@@ -627,7 +628,7 @@ int f29(int i, int j, int k, int l, int m) {
clang_analyzer_eval(m29[i].s3[1] == 1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(m29[i].s3[2] == 1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(m29[i].s3[3] == 1); // expected-warning{{UNKNOWN}}
- clang_analyzer_eval(m29[j].s3[k] == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(m29[j].s3[k] == 1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(l29->s1[m] == 2); // expected-warning{{UNKNOWN}}
// FIXME: Should warn that m29[i].s4 leaks. But not on the previous line,
// because l29 and m29 alias.
diff --git a/clang/test/Analysis/symbolic-pointer-cast.c b/clang/test/Analysis/symbolic-pointer-cast.c
new file mode 100644
index 0000000000000..9804254808343
--- /dev/null
+++ b/clang/test/Analysis/symbolic-pointer-cast.c
@@ -0,0 +1,42 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(int);
+
+void symbolic_offset_pointer_cast_preserves_nonnull(_Bool i) {
+ unsigned char a[2];
+ unsigned char *q = a + i;
+ char *r = (char *)q;
+
+ clang_analyzer_eval(q != 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(r != 0); // expected-warning{{TRUE}}
+
+ if (!r)
+ *r = 0; // no-warning
+}
+
+void differently_sized_symbolic_offset_pointer_cast_preserves_nonnull(_Bool i) {
+ int a[2];
+ int *q = a + i;
+ char *r = (char *)q;
+ int *s = (int *)r;
+
+ clang_analyzer_eval(r != 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(s == q); // expected-warning{{TRUE}}
+
+ if (!r)
+ *r = 0; // no-warning
+}
+
+void symbolic_base_pointer_cast_preserves_nonnull(int *p, _Bool i) {
+ if (!p)
+ return;
+
+ int *q = p + i;
+ char *r = (char *)q;
+
+ clang_analyzer_eval(q != 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(r != 0); // expected-warning{{TRUE}}
+
+ if (!r)
+ *r = 0; // no-warning
+}
More information about the cfe-commits
mailing list