[clang] [analyzer] Preserve LocAsInteger through identity operations (PR #225048)
Peiqi Li via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 25 22:19:20 PDT 2026
https://github.com/voyager-jhk updated https://github.com/llvm/llvm-project/pull/225048
>From ec2da4e57d1897c67fd06ae5f40aa66ff68d7e54 Mon Sep 17 00:00:00 2001
From: voyager-jhk <voyager.lpq at gmail.com>
Date: Mon, 21 Sep 2026 17:06:58 +0800
Subject: [PATCH] [analyzer] Preserve LocAsInteger through identity operations
Preserve LocAsInteger values through identity operations on integers
converted from pointers.
This fixes a false positive leak report from unix.Malloc.
Fixes #220972
---
clang/docs/ReleaseNotes.md | 3 +
.../StaticAnalyzer/Core/SimpleSValBuilder.cpp | 24 ++++++++
.../Analysis/identity-ops-on-LocAsInteger.c | 57 +++++++++++++++++++
3 files changed, 84 insertions(+)
create mode 100644 clang/test/Analysis/identity-ops-on-LocAsInteger.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index db710b64a6ae53..bb01d7718a8793 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -871,6 +871,9 @@ features cannot lower the translation-unit ABI level;
#### Crash and bug fixes
+- Fixed a false positive `unix.Malloc` leak report for identity operations on
+ integers converted from pointers. (GH#220972)
+
% comment:
% This is for the Static Analyzer.
% Use `####` headings for subsections:
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 7d154cbc840aca..c742de27e7aaa8 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -230,6 +230,25 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
return makeNonLoc(LHS, op, *ConvertedRHS, resultTy);
}
+// Returns whether X op RHS is equivalent to X.
+static bool isIdentityOperation(BinaryOperator::Opcode op,
+ const llvm::APSInt &RHS) {
+ switch (op) {
+ case BO_Add:
+ case BO_Sub:
+ case BO_Or:
+ case BO_Xor:
+ case BO_Shl:
+ case BO_Shr:
+ return RHS == 0;
+ case BO_Mul:
+ case BO_Div:
+ return RHS == 1;
+ default:
+ return false;
+ }
+}
+
// See if Sym is known to be a relation Rel with Bound.
static bool isInRelation(BinaryOperator::Opcode Rel, SymbolRef Sym,
llvm::APSInt Bound, ProgramStateRef State) {
@@ -491,6 +510,11 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
rhs.castAs<nonloc::LocAsInteger>().getLoc(),
resultTy);
case nonloc::ConcreteIntKind: {
+ // Preserve the location for identity operations.
+ if (isIdentityOperation(op,
+ rhs.castAs<nonloc::ConcreteInt>().getValue()))
+ return evalCast(lhs, resultTy, QualType{});
+
// FIXME: at the moment the implementation
// of modeling "pointers as integers" is not complete.
if (!BinaryOperator::isComparisonOp(op))
diff --git a/clang/test/Analysis/identity-ops-on-LocAsInteger.c b/clang/test/Analysis/identity-ops-on-LocAsInteger.c
new file mode 100644
index 00000000000000..39f655724f329d
--- /dev/null
+++ b/clang/test/Analysis/identity-ops-on-LocAsInteger.c
@@ -0,0 +1,57 @@
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN: -analyzer-checker=core,unix.Malloc,debug.ExprInspection
+
+// Identity operations on a LocAsInteger must preserve the original location.
+// GH#220972.
+
+typedef unsigned __INTPTR_TYPE__ uintptr_t;
+typedef __SIZE_TYPE__ size_t;
+
+void *malloc(size_t);
+void free(void *);
+void clang_analyzer_eval(int);
+
+void test_add_zero(void)
+{
+ void *ptr = malloc(16);
+ if (ptr == 0)
+ return;
+
+ uintptr_t value = (uintptr_t)ptr;
+ value += 0;
+ clang_analyzer_eval(value == (uintptr_t)ptr); // expected-warning{{TRUE}}
+
+ free((void *)value); // no-warning
+}
+
+void test_other_identity_ops(void)
+{
+ void *ptr = malloc(16);
+ if (ptr == 0)
+ return;
+
+ uintptr_t value = (uintptr_t)ptr;
+ value -= 0;
+ value |= 0;
+ value ^= 0;
+ value <<= 0;
+ value >>= 0;
+ value *= 1;
+ value /= 1;
+ clang_analyzer_eval(value == (uintptr_t)ptr); // expected-warning{{TRUE}}
+
+ free((void *)value); // no-warning
+}
+
+void test_narrower_result(void)
+{
+ void *ptr = malloc(16);
+ if (ptr == 0)
+ return;
+
+ unsigned value = (unsigned)(uintptr_t)ptr;
+ value += 0;
+ clang_analyzer_eval(value == (unsigned)(uintptr_t)ptr); // expected-warning{{TRUE}}
+
+ free(ptr); // no-warning
+}
More information about the cfe-commits
mailing list