Replace ZEND_ASSUME() by ZEND_ASSERT() in zend_hash_*_ptr setters (#14466)

I had a case where I was intentionally storing a `NULL` pointer within a
HashTable to mark an entry as “blocked”, without paying for the overhead of
checking the entry type when reading the pointer, resulting in semantics that
are similar to using `isset()` instead of `array_key_exists()` in userland.

This worked fine in unoptimized test builds, but due to the `ZEND_ASSUME()` in
the `zend_hash_find_ptr` functions, the optimized release builds turned the
logic of:

    my_pointer = zend_hash_find_ptr(ht, key);
    if (my_pointer == NULL) {
        return;
    }
    *my_pointer;

into

    zv = zend_hash_find(ht, key);
    if (zv) {
        *Z_PTR_P(zv);
    } else {
        return;
    }

thus introducing a hard-to-debug and compiler-dependent crash when the entry
exists, but the stored pointer is `NULL`.

Change the `ZEND_ASSUME()` in the setters to `ZEND_ASSERT()`. This would have
made my mistake immediately obvious in debug builds when storing the pointer.
The getters still use `ZEND_ASSUME()` under the assumption that they are called
much more often, reducing the impact on debug builds: Assuming the developer
uses the `_ptr` variants for both reading and writing the entries, the mistake
will be reliably caught during writing, making the assert during reading
unnecessary.

For release builds the `ZEND_ASSERT()` will be equivalent to `ZEND_ASSUME()`,
avoiding any performance impact for those.
This commit is contained in:
Tim Düsterhus 2024-06-05 11:08:11 +02:00 committed by GitHub
parent bda372fc6c
commit 7c2a4dbd72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -646,7 +646,7 @@ static zend_always_inline void *zend_hash_add_ptr(HashTable *ht, zend_string *ke
ZVAL_PTR(&tmp, pData);
zv = zend_hash_add(ht, key, &tmp);
if (zv) {
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
} else {
return NULL;
@ -660,7 +660,7 @@ static zend_always_inline void *zend_hash_add_new_ptr(HashTable *ht, zend_string
ZVAL_PTR(&tmp, pData);
zv = zend_hash_add_new(ht, key, &tmp);
if (zv) {
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
} else {
return NULL;
@ -674,7 +674,7 @@ static zend_always_inline void *zend_hash_str_add_ptr(HashTable *ht, const char
ZVAL_PTR(&tmp, pData);
zv = zend_hash_str_add(ht, str, len, &tmp);
if (zv) {
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
} else {
return NULL;
@ -688,7 +688,7 @@ static zend_always_inline void *zend_hash_str_add_new_ptr(HashTable *ht, const c
ZVAL_PTR(&tmp, pData);
zv = zend_hash_str_add_new(ht, str, len, &tmp);
if (zv) {
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
} else {
return NULL;
@ -701,7 +701,7 @@ static zend_always_inline void *zend_hash_update_ptr(HashTable *ht, zend_string
ZVAL_PTR(&tmp, pData);
zv = zend_hash_update(ht, key, &tmp);
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
}
@ -711,7 +711,7 @@ static zend_always_inline void *zend_hash_str_update_ptr(HashTable *ht, const ch
ZVAL_PTR(&tmp, pData);
zv = zend_hash_str_update(ht, str, len, &tmp);
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
}
@ -809,7 +809,7 @@ static zend_always_inline void *zend_hash_index_update_ptr(HashTable *ht, zend_u
ZVAL_PTR(&tmp, pData);
zv = zend_hash_index_update(ht, h, &tmp);
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
}
@ -833,7 +833,7 @@ static zend_always_inline void *zend_hash_next_index_insert_ptr(HashTable *ht, v
ZVAL_PTR(&tmp, pData);
zv = zend_hash_next_index_insert(ht, &tmp);
if (zv) {
ZEND_ASSUME(Z_PTR_P(zv));
ZEND_ASSERT(Z_PTR_P(zv));
return Z_PTR_P(zv);
} else {
return NULL;