php / php/php-src

Segfaults, inconsistency and confusion when adding (+) `int` to typecasted FFI\CData

オープン
#8,874 コメント 6 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Bug Extension: ffi Status: Needs Triage
主要言語
C
スター
40.4k
フォーク
8.1k
平均マージ
2日 13時間
マージ済み PR(30日)
96

説明

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

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

var_dump()、var_export()、FFI::string() のケースを再現し、その後、1880 行付近の Zend/zend_object_handlers.c と FFI オペランドの処理を調査します。pointer-plus-int のアクセスを繰り返した場合を比較し、型付き FFI pointers の一貫した動作を特定します。動作が定義され、報告されたケースで segmentation fault や一貫性のない TypeError が発生しなくなれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
c, php
領域
backend
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。