[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