[php-src] Issue #24068: ext/standard: conversion of uint32_t __fill_idx to zend_long in ZEND_HASH_FILL_FINISH() as used in php_array_merge_wrapper()
| From: | ushevchenko | Date: | Fri, 02 Oct 2026 05:40:56 +0000 |
| Subject: | [php-src] Issue #24068: ext/standard: conversion of uint32_t __fill_idx to zend_long in ZEND_HASH_FILL_FINISH() as used in php_array_merge_wrapper() | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-252881@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24068
Author: ushevchenko
### Description
ZEND_HASH_FILL_FINISH() in Zend/zend_hash.h assigns the uint32_t counter __fill_idx to the zend_long
field nNextFreeElement:
#define ZEND_HASH_FILL_FINISH() do { \
__fill_ht->nNumOfElements += __fill_idx - __fill_ht->nNumUsed; \
__fill_ht->nNumUsed = __fill_idx; \
__fill_ht->nNextFreeElement = __fill_idx; \
__fill_ht->nInternalPointer = 0; \
} while (0)
A static analyzer (SVACE, SIGN_EXTENSION) reports this as an unsafe uint32_t -> zend_long
conversion: if __fill_idx were greater than INT_MAX, the result would differ between 32-bit
platforms (long is 32-bit, the value becomes negative) and 64-bit platforms (long is 64-bit, the
value is zero-extended). It is reported for the use in php_array_merge_wrapper() in
ext/standard/array.c:
array_init_size(return_value, count);
dest = Z_ARRVAL_P(return_value);
zend_hash_real_init_packed(dest);
ZEND_HASH_FILL_PACKED(dest) {
ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
...
ZEND_HASH_FILL_ADD(src_entry);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
Reachability. I did not reproduce a problem; this report is based on reading the code. I believe the
conversion cannot overflow here:
- __fill_idx starts at nNumUsed of the newly created array (0) and is incremented once per element
of src, so __fill_idx <= zend_hash_num_elements(src) <= count.
- count is the sum of zend_hash_num_elements() over all arguments, and the function throws "The
total number of elements must be lower than %u" if count >= HT_MAX_SIZE.
- HT_MAX_SIZE is 0x40000000 on 64-bit and 0x02000000 on 32-bit platforms (Zend/zend_types.h), both
smaller than INT_MAX.
So __fill_idx < HT_MAX_SIZE < INT_MAX, and the value is the same after the conversion on every
platform.
Question. Is this analysis correct, i.e. can the conversion in ZEND_HASH_FILL_FINISH() be considered
safe here? If so, I will treat the report as a false positive. The same warning is reported for
other uses of ZEND_HASH_FILL_END() in ext/standard/array.c; I have analyzed only
php_array_merge_wrapper().
Found by Linux Verification Center (https://portal.linuxtesting.ru) using SVACE.
Author U. Shevchenko.
### PHP Version
```plain
8.3.31 (static analysis of the source, not run); the same code is present in master
```
### Operating System
N/A (static analysis)