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
8.3.31 (static analysis of the source, not run); the same code is present in master
Operating System
N/A (static analysis)
Description
ZEND_HASH_FILL_FINISH() in Zend/zend_hash.h assigns the uint32_t counter __fill_idx to the zend_long field nNextFreeElement:
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:
Reachability. I did not reproduce a problem; this report is based on reading the code. I believe the conversion cannot overflow here:
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
Operating System
N/A (static analysis)