[php-src] Issue #20672: FFI Memory Corruption

[email protected] (yyny)
Newsgroups php.bugs
Message-ID <[email protected]>
Issue: https://github.com/php/php-src/issues/20672
Author: yyny

### Description

After upgrading to PHP 8.5, we are getting memory corruption in our PHPUnit test suite, eventually resulting in a `SIGSEGV` or `SIGBUS`.
We initially suspected a problem in the foreign code or an ABI/calling-convention/libffi issue, as we recently changed several functions to use structs instead of C strings (Additionally, the issue only seems to occur on ARM chips?).
However, we were able to remove ALL foreign code, and narrow the issue down to an problem in PHP.

The culprit is a function that looks like this:

```php
<?php

$s = json_encode($array);

$len = strlen($s);
$cap = max($len, 1);

$buf = $this->ffi->new('unsigned char['.$cap.']'); // `unsigned char(*) buf[cap] = malloc(cap);`
FFI::memcpy($buf, $s, $len); // `memcpy(buf, s, len);`

$ptr = FFI::addr($buf[0]); // `unsigned char *ptr = &buf[0]`

$slice = $this->ffi->new('Slice'); // `Slice slice;`

$slice->ptr = $ptr; // `slice.ptr = ptr;`
// $slice->len = $len; // `slice.len = len;`

// ...rest of the code not necessary to trigger the crash.
// we don't even need to make a FFI call, just constructing a `$slice` and writing to `$slice->ptr` is enough.
```

(with `ffi` constructed as):

```php
$this->ffi = FFI::cdef(<<<EOF
typedef struct slice {
    unsigned char *ptr;
    unsigned long len;
} Slice;
EOF
);
```

When we run this function in a PHPUnit test in `lldb`, we get:

```
Process 34987 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x600000004)
    frame #0: 0x00000001000b1578 php`zend_ffi_cdata_write_field + 244
php`zend_ffi_cdata_write_field:
->  0x1000b1578 <+244>: ldr    w22, [x21]
    0x1000b157c <+248>: cmp    w22, #0xf
    0x1000b1580 <+252>: b.eq   0x1000b15e8    ; <+356>
    0x1000b1584 <+256>: ldrb   w8, [x19, #0x8]
Target 0: (php) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x600000004)
  * frame #0: 0x00000001000b1578 php`zend_ffi_cdata_write_field + 244
    frame #1: 0x00000001004ebb9c php`ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CV_TAILCALL_HANDLER + 476
    frame #2: 0x000000010059dba4 php`execute_ex + 168
    frame #3: 0x00000001005b009c php`zend_execute.cold.1 + 264
    frame #4: 0x000000010049c368 php`zend_execute + 32
    frame #5: 0x000000010056b8f8 php`zend_execute_script + 96
    frame #6: 0x00000001003ff3e4 php`php_execute_script_ex + 472
    frame #7: 0x000000010056e0d4 php`do_cli + 5404
    frame #8: 0x000000010056caac php`main + 720
    frame #9: 0x0000000184ceeb98 dyld`start + 6076
```

When compiling PHP with asan/ubsan, we also get:

```
/tmp/php-src/ext/ffi/ffi.c:1373:13: runtime error: load of value 160, which is not a valid value for type 'bool'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /tmp/php-src/ext/ffi/ffi.c:1373:13
zsh: abort      /tmp/php-src/sapi/cli/php  --configuration  --filter
```

The crash is a bit sensitive to surrounding code, unfortunately, so we can't share a full reproduction, but a couple things of note:

 - The crash happens _AFTER_ the function doing the FFI has already returned (observable since code executing after the function still executes), and instead seems to happen when the class containing `FFI` instance gets destroyed.
 - To support this, making the `FFI` instance `static` no longer gives a crash.
 - The crash _only_ happens when we enable another test to dispatch the class we are testing as a Laravel job (So basically, construct the job class, dispatch it in Laravel, construct the job class, call it normally). The first execution does not create a crash or trigger ubsan.

### PHP Version

```plain
PHP 8.5.0 (cli) (built: Nov 18 2025 08:02:20) (NTS)
Copyright (c) The PHP Group
Built by Homebrew
Zend Engine v4.5.0, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.0, Copyright (c), by Zend Technologies
```

### Operating System

Darwin macbookpro.home 24.6.0 Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:34 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T8103 arm64
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.