[clang] [clang][analyzer] Suppress core.NullDereference for offsetof macro pattern (PR #227874)
Aaditya Agarwal via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 1 21:56:43 PDT 2026
https://github.com/aaditya8979 updated https://github.com/llvm/llvm-project/pull/227874
>From 72dae6577cd73092ed17ff6d73976a7550b21e86 Mon Sep 17 00:00:00 2001
From: openhands <openhands at all-hands.dev>
Date: Thu, 1 Oct 2026 02:28:44 +0530
Subject: [PATCH] [clang][analyzer] Suppress core.NullDereference for offsetof
macro pattern
Fixes a false positive where the C90-style `offsetof` macro expansion
`&(((T*)0)->m)` triggers `DereferenceChecker`. The `ExprEngine` correctly
evaluates the `MemberExpr` as a `FieldRegion` on a null base, but we now
suppress the diagnostic if the immediate parent operation is `UO_AddrOf`,
as no physical memory load occurs.
Fixes #221768
---
.../Checkers/DereferenceChecker.cpp | 22 ++++++++++++++++-
clang/test/Analysis/offsetof-null-deref.c | 24 +++++++++++++++++++
2 files changed, 45 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Analysis/offsetof-null-deref.c
diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 979d12f1e967c..4860054dd58f9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -149,6 +150,26 @@ static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
bool DereferenceChecker::suppressReport(CheckerContext &C,
const Expr *E) const {
+ // Intercept C90-style offsetof() macro expansion: &(((T*)0)->m)
+ // The analyzer evaluates the MemberExpr as a FieldRegion on a null base,
+ // triggering checkLocation. We suppress it if the parent operation is
+ // UO_AddrOf.
+ if (const auto *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts())) {
+ if (ME->isArrow()) {
+ const Expr *Base = ME->getBase()->IgnoreParenCasts();
+ if (const auto *CE = dyn_cast<CastExpr>(Base)) {
+ if (CE->getCastKind() == CK_NullToPointer) {
+ const Stmt *Parent =
+ C.getStackFrame()->getParentMap().getParentIgnoreParenCasts(E);
+ if (const auto *UO = dyn_cast_or_null<UnaryOperator>(Parent)) {
+ if (UO->getOpcode() == UO_AddrOf)
+ return true;
+ }
+ }
+ }
+ }
+ }
+
// Do not report dereferences on memory that use address space #256, #257,
// and #258. Those address spaces are used when dereferencing address spaces
// relative to the GS, FS, and SS segments on x86/x86-64 targets.
@@ -157,7 +178,6 @@ bool DereferenceChecker::suppressReport(CheckerContext &C,
// are defined as an error unless explicitly defined.
// See https://clang.llvm.org/docs/LanguageExtensions.html, the section
// "X86/X86-64 Language Extensions"
-
QualType Ty = E->getType();
if (!Ty.hasAddressSpace())
return false;
diff --git a/clang/test/Analysis/offsetof-null-deref.c b/clang/test/Analysis/offsetof-null-deref.c
new file mode 100644
index 0000000000000..aeb98dd8637d6
--- /dev/null
+++ b/clang/test/Analysis/offsetof-null-deref.c
@@ -0,0 +1,24 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core.NullDereference %s
+// expected-no-diagnostics
+
+typedef unsigned long size_t;
+
+struct Point {
+ int x;
+ int y;
+};
+
+#define MY_OFFSETOF(T, m) ((size_t)(&((T*)0)->m))
+
+size_t get_y_offset(void) {
+ return MY_OFFSETOF(struct Point, y);
+}
+
+struct Outer {
+ int pad;
+ struct Point pt;
+};
+
+size_t get_nested_offset(void) {
+ return MY_OFFSETOF(struct Outer, pt.x);
+}
More information about the cfe-commits
mailing list