Skip to content

ext/standard: conversion of uint32_t __fill_idx to zend_long in ZEND_HASH_FILL_FINISH() as used in php_array_merge_wrapper() #24068

Description

@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

8.3.31 (static analysis of the source, not run); the same code is present in master

Operating System

N/A (static analysis)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions