[php-src] Issue #8874: Segfaults, inconsistency and confusion when adding (+) `int` to typecasted FFI\CData
| From: | exikyut | Date: | Sun, 26 Jun 2022 09:07:14 +0000 |
| Subject: | [php-src] Issue #8874: Segfaults, inconsistency and confusion when adding (+) `int` to typecasted FFI\CData | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-241811@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/8874
Author: exikyut
### Description
The following code:
```php
<?php
$ffi = FFI::cdef('
typedef long unsigned int size_t;
void *malloc(size_t size);
');
$ptr = $ffi->cast('uint8_t *', $ffi->malloc(8));
print "A: "; var_dump($ptr);
print "B: "; var_dump($ptr + 1);
print "C: "; var_dump($ptr);
print "D: "; var_dump($ptr + 1);
```
Resulted in this output:
```
A: object(FFI\CData:uint8_t*)#4 (1) {
[0]=>
int(0)
}
B: object(FFI\CData:uint8_t*)#5 (1) {
[0]=>
int(0)
}
C: Segmentation fault
```
But I expected this output instead:
*Not sure what would be canonically correct*
I get the following backtrace:
```
#0 0x00007ffff43a7915 in ?? () from /usr/lib/php/20210902/ffi.so
#1 0x00005555558be86d in zend_std_get_properties_for (obj=<optimized out>,
purpose=<optimized out>) at ./Zend/zend_object_handlers.c:1880
#2 0x00005555557a77e1 in php_var_dump (struc=0x7ffff5413530, level=1) at ./ext/standard/var.c:163
#3 0x00005555557a7ce1 in zif_var_dump (execute_data=<optimized out>,
return_value=<optimized out>) at ./ext/standard/var.c:228
#4 0x000055555589797b in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER () at
./Zend/zend_vm_execute.h:1235
#5 execute_ex (ex=0x7ffff5455e40) at ./Zend/zend_vm_execute.h:55752
#6 0x000055555589eced in zend_execute (op_array=0x7ffff548c000, return_value=0x0) at
./Zend/zend_vm_execute.h:60123
#7 0x00005555558311cd in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0,
file_count=file_count@entry=3) at ./Zend/zend.c:1790
#8 0x00005555557ccc71 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd760) at
./main/main.c:2538
#9 0x0000555555915673 in do_cli (argc=2, argv=0x555555ac5390) at ./sapi/cli/php_cli.c:969
#10 0x0000555555677f11 in main (argc=2, argv=0x555555ac5390) at ./sapi/cli/php_cli.c:1371
```
Here's line 1880:
https://github.com/php/php-src/blob/PHP-8.1.7/Zend/zend_object_handlers.c#L1880
### Additionally,
*This* code (which swaps
var_dump() for var_export(), to take the
alternate path through zend_std_get_properties_for()):
```php
<?php
$ffi = FFI::cdef('
typedef long unsigned int size_t;
void *malloc(size_t size);
');
$ptr = $ffi->cast('uint8_t *', $ffi->malloc(8));
print "A: "; var_export($ptr);
print "\nB: "; var_export($ptr + 1);
print "\nC: "; var_export($ptr);
print "\nD: "; var_export($ptr + 1);
```
Results in this output:
```
A: FFI\CData::__set_state(array(
))
B: FFI\CData::__set_state(array(
))
C: FFI\CData::__set_state(array(
))
D: PHP Fatal error: Uncaught TypeError: Unsupported operand types: FFI\CData + int
```
But I expected this output instead:
*Not sure what would be canonically correct*
### hOwEvEr,
***ThIs*** code:
```php
<?php
$ffi = FFI::cdef('
typedef long unsigned int size_t;
void *malloc(size_t size);
');
$cast = $ffi->cast('uint8_t *', $ffi->malloc(27));
// Fill memory with "A..Z" for presentational purposes
for ($i = 0; $i < 26; $i++) {
$cast[$i] = $i + 65; // 'A'
}
var_dump(FFI::string($cast, 16));
var_dump(FFI::string($cast + 5, 16));
var_dump(FFI::string($cast + 5, 16));
```
Produces ***this*** output!!
```
string(16) "ABCDEFGHIJKLMNOP"
string(16) "FGHIJKLMNOPQRSTU"
PHP Fatal error: Uncaught TypeError: Unsupported operand types: FFI\CData + int
```
---
Commentary:
I'm currently using the FFI to mmap() some data into memory (for both read and
write). Reading and writing integer values (typecasted to uint16_t/uint32_t) is working perfectly
fine (thanks for plumbing everything out so this is as simple as assigning to a PHP array :D), but
now I needed to read and write sequences of characters (binary data I want written to specific
offsets).
Naturally that's done in C by just assigning to ptr + offset (where ptr might be
uint8_t * and offset is int), so I wondered if the same worked here. It...
\*does\*, I think...? Except it crashes. This is why I said I don't know what the
canonically-correct outcome here is: this is currently Schrodinger's architecture, where
I'm equally observing "yes that's the right way to do it" and \**crash*\* and I
don't know where to put the consensus :)
I was reading the values I wanted to mmap() using (PHP-side) fread() so
I'll either just stick with that or play with FFI::memcpy() for now. Thanks again for FFI,
it's really awesome.
### PHP Version
PHP 8.1.7 from sury.org
### Operating System
Debian 11.3