[clang] Null field access (PR #69223)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 16 08:55:03 PDT 2023
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID: <llvm/llvm-project/pull/69223/clang at github.com>
In-Reply-To:
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/69223
Looks like this should work as long as we don't dereference the value.
Note: This is a change from my local branch where I introduce integral pointers. One thing I changed is that null pointers are integral pointers and not block pointers, but in this version they are still block pointers so we have to be careful about `Pointee` being null.
>From b20da36444adfbdbddd6d0dddf06535815ffe0c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 3 Sep 2023 07:03:04 +0200
Subject: [PATCH 1/2] [clang][Interp] Check pointer inc/dec ops for null
---
clang/lib/AST/Interp/Interp.h | 7 +++++--
clang/test/AST/Interp/arrays.cpp | 20 ++++++++++++++++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index e3e6a4cec63b194..3d226a40f9cf608 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1488,11 +1488,14 @@ static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC,
const Pointer &Ptr) {
using OneT = Integral<8, false>;
+ const Pointer &P = Ptr.deref<Pointer>();
+ if (!CheckNull(S, OpPC, P, CSK_ArrayIndex))
+ return false;
+
// Get the current value on the stack.
- S.Stk.push<Pointer>(Ptr.deref<Pointer>());
+ S.Stk.push<Pointer>(P);
// Now the current Ptr again and a constant 1.
- Pointer P = Ptr.deref<Pointer>();
OneT One = OneT::from(1);
if (!OffsetHelper<OneT, Op>(S, OpPC, One, P))
return false;
diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp
index 281835f828bbd7c..18c4ae4354f54a0 100644
--- a/clang/test/AST/Interp/arrays.cpp
+++ b/clang/test/AST/Interp/arrays.cpp
@@ -333,6 +333,26 @@ namespace IncDec {
// expected-note {{in call to}} \
// ref-error {{not an integral constant expression}} \
// ref-note {{in call to}}
+
+ constexpr int nullptr1(bool Pre) {
+ int *a = nullptr;
+ if (Pre)
+ ++a; // ref-note {{arithmetic on null pointer}} \
+ // expected-note {{arithmetic on null pointer}}
+ else
+ a++; // ref-note {{arithmetic on null pointer}} \
+ // expected-note {{arithmetic on null pointer}}
+ return 1;
+ }
+ static_assert(nullptr1(true) == 1, ""); // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}}
+
+ static_assert(nullptr1(false) == 1, ""); // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}}
};
namespace ZeroInit {
>From a1d79cbe5c3ad257b44beb0ba3b8cb3e0dcbe84a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 16 Oct 2023 17:51:44 +0200
Subject: [PATCH 2/2] [clang][Interp] Only diagnose null field access in
constant contexts
---
clang/lib/AST/Interp/Interp.h | 2 +-
clang/lib/AST/Interp/Pointer.h | 4 +++-
clang/test/AST/Interp/c.c | 12 +++++++++++
clang/test/AST/Interp/records.cpp | 33 +++++++++++++++++++++++++++++++
4 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 3d226a40f9cf608..9258442aaaa1d4d 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1159,7 +1159,7 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
/// 2) Pushes Pointer.atField(Off) on the stack
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
- if (!CheckNull(S, OpPC, Ptr, CSK_Field))
+ if (S.inConstantContext() && !CheckNull(S, OpPC, Ptr, CSK_Field))
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index d5279e757f04764..c6a2f371764d02f 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -295,7 +295,7 @@ class Pointer {
bool isUnion() const;
/// Checks if the storage is extern.
- bool isExtern() const { return Pointee->isExtern(); }
+ bool isExtern() const { return Pointee && Pointee->isExtern(); }
/// Checks if the storage is static.
bool isStatic() const { return Pointee->isStatic(); }
/// Checks if the storage is temporary.
@@ -348,6 +348,8 @@ class Pointer {
/// Checks if the index is one past end.
bool isOnePastEnd() const {
+ if (!Pointee)
+ return false;
return isElementPastEnd() || getSize() == getOffset();
}
diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c
index 974ca72702f7dd0..637915328576af1 100644
--- a/clang/test/AST/Interp/c.c
+++ b/clang/test/AST/Interp/c.c
@@ -47,3 +47,15 @@ _Static_assert(&a != 0, ""); // ref-warning {{always true}} \
// expected-warning {{always true}} \
// pedantic-expected-warning {{always true}} \
// pedantic-expected-warning {{is a GNU extension}}
+
+struct y {int x,y;};
+int a2[(long)&((struct y*)0)->y]; // expected-warning {{folded to constant array}} \
+ // pedantic-expected-warning {{folded to constant array}} \
+ // ref-warning {{folded to constant array}} \
+ // pedantic-ref-warning {{folded to constant array}}
+
+const struct y *yy = (struct y*)0;
+const long L = (long)(&(yy->y)); // expected-error {{not a compile-time constant}} \
+ // pedantic-expected-error {{not a compile-time constant}} \
+ // ref-error {{not a compile-time constant}} \
+ // pedantic-ref-error {{not a compile-time constant}}
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index bcc84087fc54020..88c9c5ae7db5553 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1066,3 +1066,36 @@ namespace ParenInit {
constexpr B b(A(1),2);
}
#endif
+
+namespace AccessOnNullptr {
+ struct F {
+ int a;
+ };
+
+ constexpr int a() { // expected-error {{never produces a constant expression}} \
+ // ref-error {{never produces a constant expression}}
+ F *f = nullptr;
+
+ f->a = 0; // expected-note 2{{cannot access field of null pointer}} \
+ // ref-note 2{{cannot access field of null pointer}}
+ return f->a;
+ }
+ static_assert(a() == 0, ""); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to 'a()'}} \
+ // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to 'a()'}}
+
+ constexpr int a2() { // expected-error {{never produces a constant expression}} \
+ // ref-error {{never produces a constant expression}}
+ F *f = nullptr;
+
+
+ const int *a = &(f->a); // expected-note 2{{cannot access field of null pointer}} \
+ // ref-note 2{{cannot access field of null pointer}}
+ return f->a;
+ }
+ static_assert(a2() == 0, ""); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to 'a2()'}} \
+ // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to 'a2()'}}
+}
More information about the cfe-commits
mailing list