Segfaults, inconsistency and confusion when adding (+) `int` to typecasted FFI\CData
Personne n'a encore pris cette issue.
- Langage dominant
- C
- Étoiles
- 40.4k
- Forks
- 8.1k
- Merge moyen
- 2 j 13 h
- PR mergées (30 j)
- 96
Description
Description
The following code:
<?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
$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
$ffi = FFI::cdef('
typedef long unsigned int size_t;
void *malloc(size_t size);
');
$ptr = $ffi->cast('uint8_t *', $ffi->malloc(27));
// Fill memory with "A..Z" for presentational purposes
for ($i = 0; $i < 26; $i++) {
$ptr[$i] = $i + 65; // 'A'
}
var_dump(FFI::string($ptr, 16));
var_dump(FFI::string($ptr + 5, 16));
var_dump(FFI::string($ptr + 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
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Reproduisez les cas var_dump(), var_export() et FFI::string(), puis inspectez Zend/zend_object_handlers.c autour de la ligne 1880 ainsi que la gestion des opérandes FFI. Comparez les accès répétés de type pointer-plus-int et déterminez le comportement cohérent pour les FFI pointers typés. La tâche est terminée lorsque le comportement est défini et que les cas signalés ne produisent plus de segmentation fault ni de TypeError incohérent.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- c, php
- Domaine
- backend
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 35/100