From 3c2446d09ae1b60488fcb1d30d0feca95b220f7f Mon Sep 17 00:00:00 2001 From: Tobi Laskowski Date: Thu, 30 Apr 2026 00:28:15 +0100 Subject: [PATCH 1/4] Preserve pointer type in ZEND_MM_ALIGNED_BASE On some architectures, e.g. CHERI, pointers are wider than size_t so converting to size_t causes part of the pointer to be lost, which means it cannot be cast back into a pointer. It can be preserved by casting to uintptr_t instead. --- Zend/zend_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 942a8b8e1309..a11dbcf37fbc 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -177,8 +177,8 @@ typedef zend_ulong zend_mm_bitset; /* 4-byte or 8-byte integer */ #define ZEND_MM_ALIGNED_OFFSET(size, alignment) \ (((size_t)(size)) & ((alignment) - 1)) -#define ZEND_MM_ALIGNED_BASE(size, alignment) \ - (((size_t)(size)) & ~((alignment) - 1)) +#define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ + (((uintptr_t)(ptr)) & ~((alignment) - 1)) #define ZEND_MM_SIZE_TO_NUM(size, alignment) \ (((size_t)(size) + ((alignment) - 1)) / (alignment)) From eb4b0d76f3b8999b0e1b8c41c09f0cad80331d2c Mon Sep 17 00:00:00 2001 From: Tobi Laskowski Date: Wed, 5 Nov 2025 14:23:28 +0000 Subject: [PATCH 2/4] Use alignment builtins for ZEND_MM_ALIGNED macros Where available, these provide a more descriptive way of aligning pointers than direct bitwise manipulation. They are also more compatible with platforms like CHERI. --- Zend/zend_alloc.c | 19 ++++++++++++++++--- build/php.m4 | 2 ++ configure.ac | 2 ++ win32/build/config.w32 | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index a11dbcf37fbc..9213c2744c41 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -175,12 +175,25 @@ static size_t _real_page_size = ZEND_MM_PAGE_SIZE; typedef uint32_t zend_mm_page_info; /* 4-byte integer */ typedef zend_ulong zend_mm_bitset; /* 4-byte or 8-byte integer */ -#define ZEND_MM_ALIGNED_OFFSET(size, alignment) \ +#ifdef PHP_HAVE_BUILTIN_ALIGN_DOWN +# define ZEND_MM_ALIGNED_OFFSET(size, alignment) \ + (((size_t)(size)) - (size_t)__builtin_align_down((size), (alignment))) +# define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ + __builtin_align_down((ptr), (alignment)) +#else +# define ZEND_MM_ALIGNED_OFFSET(size, alignment) \ (((size_t)(size)) & ((alignment) - 1)) -#define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ +# define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ (((uintptr_t)(ptr)) & ~((alignment) - 1)) -#define ZEND_MM_SIZE_TO_NUM(size, alignment) \ +#endif + +#ifdef PHP_HAVE_BUILTIN_ALIGN_UP +# define ZEND_MM_SIZE_TO_NUM(size, alignment) \ + (__builtin_align_up((size), (alignment)) / (alignment)) +#else +# define ZEND_MM_SIZE_TO_NUM(size, alignment) \ (((size_t)(size) + ((alignment) - 1)) / (alignment)) +#endif #define ZEND_MM_BITSET_LEN (sizeof(zend_mm_bitset) * 8) /* 32 or 64 */ #define ZEND_MM_PAGE_MAP_LEN (ZEND_MM_PAGES / ZEND_MM_BITSET_LEN) /* 16 or 8 */ diff --git a/build/php.m4 b/build/php.m4 index 516e18e1abfb..e33e73c953ad 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -2380,6 +2380,8 @@ AC_CACHE_CHECK([for $1], [php_var], [__builtin_ssubll_overflow], [long long tmpvar; return $1(3, 7, &tmpvar);], [__builtin_unreachable], [$1();], [__builtin_usub_overflow], [unsigned int tmpvar; return $1(3, 7, &tmpvar);], + [__builtin_align_down], [return $1(1, 2) == 0 ? 1 : 0;], + [__builtin_align_up], [return $1(1, 2) == 2 ? 1 : 0;], [ m4_warn([syntax], [Unsupported builtin '$1', the test may fail.]) $1(); diff --git a/configure.ac b/configure.ac index 6c517ecc0a1e..4f69694ded7e 100644 --- a/configure.ac +++ b/configure.ac @@ -490,6 +490,8 @@ PHP_CHECK_BUILTIN([__builtin_ssubl_overflow]) PHP_CHECK_BUILTIN([__builtin_ssubll_overflow]) PHP_CHECK_BUILTIN([__builtin_unreachable]) PHP_CHECK_BUILTIN([__builtin_usub_overflow]) +PHP_CHECK_BUILTIN([__builtin_align_down]) +PHP_CHECK_BUILTIN([__builtin_align_up]) dnl Check AVX512 PHP_CHECK_AVX512_SUPPORTS diff --git a/win32/build/config.w32 b/win32/build/config.w32 index aefcfb5f8247..9426c566f133 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -370,6 +370,8 @@ if (VS_TOOLSET) { AC_DEFINE("PHP_HAVE_BUILTIN_SSUBLL_OVERFLOW", 1, "Define to 1 if the compiler supports '__builtin_ssubll_overflow'."); AC_DEFINE("PHP_HAVE_BUILTIN_SMULL_OVERFLOW", 1, "Define to 1 if the compiler supports '__builtin_smull_overflow '."); AC_DEFINE("PHP_HAVE_BUILTIN_SMULLL_OVERFLOW", 1, "Define to 1 if the compiler supports '__builtin_smulll_overflow'."); + AC_DEFINE("PHP_HAVE_BUILTIN_ALIGN_DOWN", 1, "Define to 1 if the compiler supports '__builtin_align_down'."); + AC_DEFINE("PHP_HAVE_BUILTIN_ALIGN_UP", 1, "Define to 1 if the compiler supports '__builtin_align_up'."); if (PHP_SANITIZER == "yes") { if (COMPILER_NUMERIC_VERSION < 500) { From 526ee4f74635e4e479ae82bdbcfec1866bb2359a Mon Sep 17 00:00:00 2001 From: Tobi Laskowski Date: Fri, 1 May 2026 21:52:57 +0100 Subject: [PATCH 3/4] Adjust ZEND_MM_ALIGNED_OFFSET argument name Since it is clear that it is a pointer now, we may use ZEND_MM_ALIGNED_BASE for the builtin implementation. --- Zend/zend_alloc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 9213c2744c41..d5351113d7cb 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -176,15 +176,15 @@ typedef uint32_t zend_mm_page_info; /* 4-byte integer */ typedef zend_ulong zend_mm_bitset; /* 4-byte or 8-byte integer */ #ifdef PHP_HAVE_BUILTIN_ALIGN_DOWN -# define ZEND_MM_ALIGNED_OFFSET(size, alignment) \ - (((size_t)(size)) - (size_t)__builtin_align_down((size), (alignment))) # define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ __builtin_align_down((ptr), (alignment)) +# define ZEND_MM_ALIGNED_OFFSET(ptr, alignment) \ + (((size_t)(ptr)) - (size_t)ZEND_MM_ALIGNED_BASE(ptr, alignment)) #else -# define ZEND_MM_ALIGNED_OFFSET(size, alignment) \ - (((size_t)(size)) & ((alignment) - 1)) # define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ (((uintptr_t)(ptr)) & ~((alignment) - 1)) +# define ZEND_MM_ALIGNED_OFFSET(ptr, alignment) \ + (((size_t)(ptr)) & ((alignment) - 1)) #endif #ifdef PHP_HAVE_BUILTIN_ALIGN_UP From f391cd7a391fb5b79fe84c468b9000826f090352 Mon Sep 17 00:00:00 2001 From: Tobi Laskowski Date: Sun, 3 May 2026 19:25:35 +0100 Subject: [PATCH 4/4] Ensure consistent type for ZEND_MM_ALIGNED_BASE Now it always produces a void* value across the builtin and bitwise operator implementations. --- Zend/zend_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index d5351113d7cb..84affd31ed5f 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -177,12 +177,12 @@ typedef zend_ulong zend_mm_bitset; /* 4-byte or 8-byte integer */ #ifdef PHP_HAVE_BUILTIN_ALIGN_DOWN # define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ - __builtin_align_down((ptr), (alignment)) + __builtin_align_down((void*)(ptr), (alignment)) # define ZEND_MM_ALIGNED_OFFSET(ptr, alignment) \ (((size_t)(ptr)) - (size_t)ZEND_MM_ALIGNED_BASE(ptr, alignment)) #else # define ZEND_MM_ALIGNED_BASE(ptr, alignment) \ - (((uintptr_t)(ptr)) & ~((alignment) - 1)) + ((void*)(((uintptr_t)(ptr)) & ~((alignment) - 1))) # define ZEND_MM_ALIGNED_OFFSET(ptr, alignment) \ (((size_t)(ptr)) & ((alignment) - 1)) #endif