Bug #72936 [NEW]: Zend API's zend_symtable_str_update() asserts key should end with '\0'
| From: | arseny dot vakhrushev at gmail dot com | Date: | Thu, 25 Aug 2016 06:15:52 +0000 |
| Subject: | Bug #72936 [NEW]: Zend API's zend_symtable_str_update() asserts key should end with '\0' | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-203529@lists.php.net to get a copy of this message | ||
From: arseny dot vakhrushev at gmail dot com
Operating system: All
PHP version: 7.0.10
Package: Arrays related
Bug Type: Bug
Bug description:Zend API's zend_symtable_str_update() asserts key should end with
'\0'
Description:
------------
Hello everyone,
I'm a developer of a PHP extension, namely php-amf3, and I'm having an
issue with zend_symtable_str_update() defined in Zend/zend_hash.h. The
code seems to be encouraged to use zend_symtable_str_update() instead of
zend_hash_str_update() for string keys because otherwise in case of
"numeric" string keys like "123", "-10", etc, there is no way to index
such arrays in PHP scripts when using zend_hash_str_update(). More
specifically, a C code:
...
zval arr, val;
array_init(&arr);
ZVAL_TRUE(&val);
zend_hash_str_update(HASH_OF(&arr), "123", 3, &val); //
zend_symtable_str_update() needs to be used instead
...
builds an array ["123" => true] which is impossible to index as
$arr["123"]. Internally, the key is tested whether it's "numeric" (which
is the case) and then the array is indexed with an integer value of 123
which yields NULL.
Ok, switching to zend_symtable_str_update() instead of
zend_hash_str_update() seems like a Sunday picnic in the park. They are
compatible, and everything looks neat and tidy.
The problem arises when PHP is compiled with assertions (my default
development mode). zend_symtable_str_update() is essentially a wrapper
around zend_hash_index_update() and zend_hash_str_update() calling
either of them depending on the provided key being "numeric" or not:
zval *zend_symtable_str_update(HashTable *ht, const char *str, size_t
len, zval *pData)
{
zend_ulong idx;
if (ZEND_HANDLE_NUMERIC_STR(str, len, idx)) {
return zend_hash_index_update(ht, idx, pData);
} else {
return zend_hash_str_update(ht, str, len, pData);
}
}
The ZEND_HANDLE_NUMERIC_STR macro is essentially a call to:
int _zend_handle_numeric_str_ex(const char *key, size_t length,
zend_ulong *idx)
{
register const char *tmp = key;
const char *end = key + length;
ZEND_ASSERT(*end == '\0');
...
}
The problem lies in the ZEND_ASSERT which incurs the following:
- It reads beyond 'length' bytes (in contrast, zend_hash_str_update()
does not);
- Requests keys to have a trailing zero making the whole point of the
'length' argument useless (please note that it DOES NOT rely on a key to
end with '\0' anywhere else in the body);
- Makes it impossible to avoid allocating a temporary buffer in the case
when a source key buffer does not have a trailing zero.
It seems that the whole point of having a ZEND_ASSERT here does not make
a lot of sense since there is no similar restriction in the underlying
zend_hash_str_update() function.
I infer that this assertion has been there for some "historical" reasons
from the era of trailing zeroes in strings/keys and should be removed.
Of course, one can work around the whole issue by allocating a temporary
buffer, copying a key into it, finalizing it with a trailing zero and
calling zend_symtable_str_update() with it. But I hope everyone can see
that little issues like this lead to more issues in the future
essentially masking problems at the expense of the user (additional
overhead) instead of solving them.
Cheers,
Arseny
--
Edit bug report at https://bugs.php.net/bug.php?id=72936&edit=1
--
Try a snapshot (PHP 5.4): https://bugs.php.net/fix.php?id=72936&r=trysnapshot54
Try a snapshot (PHP 5.5): https://bugs.php.net/fix.php?id=72936&r=trysnapshot55
Try a snapshot (trunk): https://bugs.php.net/fix.php?id=72936&r=trysnapshottrunk
Fixed in SVN: https://bugs.php.net/fix.php?id=72936&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=72936&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=72936&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=72936&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=72936&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=72936&r=support
Expected behavior: https://bugs.php.net/fix.php?id=72936&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=72936&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=72936&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=72936&r=globals
PHP 4 support discontinued: https://bugs.php.net/fix.php?id=72936&r=php4
Daylight Savings: https://bugs.php.net/fix.php?id=72936&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=72936&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=72936&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=72936&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=72936&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=72936&r=mysqlcfg