From 2f30c08a0b95e1e75835176e42fd924811f78349 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Thu, 6 Aug 2026 10:05:02 +0000 Subject: [PATCH 1/2] Zend: compile time assert on Bucket size In zend_compile.c, flags are stored in the lower bits of the Bucket address. If Bucket is aligned to 8 bytes, the lower three bits are always zero and this gives no problems. If the Bucket is not aligned, this results in non-obvious errors because the memory address and the flags overlap. This is difficult to debug when it happens, so add this assertion to make it more obvious what is wrong. The flags are ZEND_BIND_REF, ZEND_BIND_IMPLICIT, ZEND_BIND_EXPLICIT. Related to https://github.com/php/php-src/pull/19079 --- Zend/zend_types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index b30f53d7f917..e879567772d8 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -389,6 +389,8 @@ typedef struct _Bucket { zend_string *key; /* string key or NULL for numerics */ } Bucket; +ZEND_STATIC_ASSERT(sizeof(Bucket) % 8 == 0, "Bucket size not compatible with storing flags in lower three bits"); + typedef struct _zend_array HashTable; struct _zend_array { From 31e05c8cbd61cbb8d220db19d6921cb847bc9d9f Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Sat, 8 Aug 2026 14:38:35 +0000 Subject: [PATCH 2/2] Move assertion close to where flags are stored Instead of close to Bucket. This way, it is clearer what to look for if the assertion fails. --- Zend/zend_compile.c | 2 ++ Zend/zend_types.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a2f126fb101d..882b1bf990bf 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5916,6 +5916,8 @@ static void zend_compile_static_var_common(zend_string *var_name, zval *value, u opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, NULL); opline->op1_type = IS_CV; opline->op1.var = lookup_cv(var_name); + + ZEND_STATIC_ASSERT(sizeof(Bucket) % 8 == 0, "Bucket size not compatible with storing flags in lower three bits"); opline->extended_value = (uint32_t)((char*)value - (char*)CG(active_op_array)->static_variables->arData) | mode; } /* }}} */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index e879567772d8..b30f53d7f917 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -389,8 +389,6 @@ typedef struct _Bucket { zend_string *key; /* string key or NULL for numerics */ } Bucket; -ZEND_STATIC_ASSERT(sizeof(Bucket) % 8 == 0, "Bucket size not compatible with storing flags in lower three bits"); - typedef struct _zend_array HashTable; struct _zend_array {