[clang] [Clang] Fix crash in alignment builtins with null pointers (PR #224549)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 19 09:06:59 PDT 2026
https://github.com/keepyixiao updated https://github.com/llvm/llvm-project/pull/224549
>From 0272294e5d59e04b466d6ad215fdf06b86275ff1 Mon Sep 17 00:00:00 2001
From: yixiao <yixiao at hygon.cn>
Date: Fri, 18 Sep 2026 15:50:42 +0800
Subject: [PATCH] [Clang] Fix crash in alignment builtins with null pointers
Clang can crash when evaluating __builtin_align_up,
__builtin_align_down, or __builtin_is_aligned with a null pointer.
Handle base-less pointers without querying their base alignment, and use
the numeric pointer value when possible.
Add regression tests for null pointer operands.
---
clang/docs/ReleaseNotes.md | 2 ++
clang/lib/AST/ExprConstant.cpp | 29 +++++++++++++++++++++++++++++
clang/test/Sema/builtin-align.c | 21 +++++++++++++++++++++
3 files changed, 52 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index f4a34a37aff52e..cc90d8ad69ac4d 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -562,6 +562,8 @@ features cannot lower the translation-unit ABI level;
reference to a vector type; `vec_step` (in C++ for OpenCL) and
`__builtin_ptrauth_type_discriminator` similarly no longer accept reference
types that their evaluation silently mishandled. (#GH216997)
+- Fixed a crash when constant-evaluating `__builtin_align_up`, `__builtin_align_down`,
+ or `__builtin_is_aligned` with a pointer that has no underlying object, including null pointers. (#GH224549)
#### Bug Fixes to Attribute Support
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 92424918328419..609009187ca469 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10695,6 +10695,24 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
if (!getAlignmentArgument(E->getArg(1), E->getArg(0)->getType(), Info,
Alignment))
return false;
+
+ // A base-less pointer has a known numeric address stored in Offset, but
+ // there is no underlying object relative to which the pointer can be
+ // adjusted. If the address is already sufficiently aligned, the builtin
+ // is a no-op and the original pointer value can be preserved.
+ if (!Result.Base) {
+ assert(Alignment.getBitWidth() <= 64 &&
+ "Cannot handle > 64-bit address-space");
+ uint64_t Alignment64 = Alignment.getZExtValue();
+ uint64_t PointerValue = Result.Offset.getQuantity();
+ if (llvm::isAligned(llvm::Align(Alignment64), PointerValue))
+ return true;
+
+ Info.FFDiag(E->getArg(0), diag::note_constexpr_alignment_adjust)
+ << Alignment;
+ return false;
+ }
+
CharUnits BaseAlignment = getBaseAlignment(Info, Result);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Result.Offset);
// For align_up/align_down, we can return the same value if the alignment
@@ -17072,6 +17090,17 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
// If we evaluated a pointer, check the minimum known alignment.
LValue Ptr;
Ptr.setFrom(Info.Ctx, Src);
+ // Pointers without a base have a known numeric address, so check that
+ // address directly instead of trying to determine a base alignment.
+ if (!Ptr.Base) {
+ assert(Alignment.getBitWidth() <= 64 &&
+ "Cannot handle > 64-bit address-space");
+ uint64_t Alignment64 = Alignment.getZExtValue();
+ uint64_t PointerValue = Ptr.Offset.getQuantity();
+ return Success(
+ llvm::isAligned(llvm::Align(Alignment64), PointerValue) ? 1 : 0, E);
+ }
+
CharUnits BaseAlignment = getBaseAlignment(Info, Ptr);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset);
// We can return true if the known alignment at the computed offset is
diff --git a/clang/test/Sema/builtin-align.c b/clang/test/Sema/builtin-align.c
index c33ad8d1ad0ef8..bbd122e48e7b2b 100644
--- a/clang/test/Sema/builtin-align.c
+++ b/clang/test/Sema/builtin-align.c
@@ -115,6 +115,12 @@ void constant_expression(int x) {
_Static_assert(!__builtin_is_aligned(256, 512ULL), "");
_Static_assert(__builtin_align_up(33, 32) == 64, "");
_Static_assert(__builtin_align_down(33, 32) == 32, "");
+ _Static_assert(__builtin_is_aligned((void *)0, 1), ""); // expected-warning {{checking whether a value is aligned to 1 byte is always true}}
+ _Static_assert(__builtin_is_aligned((void *)0, 32), "");
+ _Static_assert(__builtin_is_aligned((void *)32, 32), ""); // expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+ // expected-note at -1 {{this conversion is not allowed in a constant expression}}
+ _Static_assert(!__builtin_is_aligned((void *)32, 64), ""); // expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+ // expected-note at -1 {{this conversion is not allowed in a constant expression}}
// But not if one of the arguments isn't constant:
_Static_assert(ALIGN_BUILTIN(33, x) != 100, ""); // expected-error {{static assertion expression is not an integral constant expression}}
@@ -125,6 +131,21 @@ void constant_expression(int x) {
int global1 = __builtin_align_down(33, 8);
int global2 = __builtin_align_up(33, 8);
_Bool global3 = __builtin_is_aligned(33, 8);
+_Bool global4 = __builtin_is_aligned((void *)33, 8);
+_Bool global5 = __builtin_is_aligned((void *)32, 32);
+_Bool global6 = __builtin_is_aligned((void *)32, 64);
+
+// Zero-valued null pointers are already aligned and should remain unchanged.
+void *null_align_up_1 = __builtin_align_up((void *)0, 1); // expected-warning {{aligning a value to 1 byte is a no-op}}
+void *null_align_up_32 = __builtin_align_up((void *)0, 32);
+void *null_align_down_1 = __builtin_align_down((void *)0, 1); // expected-warning {{aligning a value to 1 byte is a no-op}}
+void *null_align_down_32 = __builtin_align_down((void *)0, 32);
+
+// Check alignment builtins with non-zero integer-derived pointers.
+void *num_align_up_32 = __builtin_align_up((void *)32, 32);
+void *num_align_up_64 = __builtin_align_up((void *)32, 64); // expected-error {{initializer element is not a compile-time constant}}
+void *num_align_down_32 = __builtin_align_down((void *)32, 32);
+void *num_align_down_64 = __builtin_align_down((void *)32, 64); // expected-error {{initializer element is not a compile-time constant}}
extern void test_ptr(char *c);
char *test_array_and_fnptr(void) {
More information about the cfe-commits
mailing list