-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Throw in FFI::addr() when referencing temporary pointer #9601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Throw in FFI::addr() when referencing temporary pointer #9601
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's okay. At least I cannot imagine any possible value from taking the address of a rc=1 adress.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fine.
The error message might be done more clear e.g. "Cannot take FFI::addr() from temporary poiner", but I'm not sure.
…rface is rounded down since PHP 8.1.0
I just tried upgrading my FFI code to PHP 8.3 and it ... blew up, thanks to this. More precisely, my code looks like It can be worked around by assigning $cdata->struct_member to a temp var by reference, but that's even uglier? What this PR probably wanted to solve is catching when the owned value is destroyed. I'm not sure how to do that, but I think we may need to revert the arbitrarily restrictive behaviour of this PR? (The irony that I initially approved this is not lost on me.) |
@bwoebi could you please provide the complete reproduction case. |
Sure @dstogov, simplified reproducer:
Note the difference, in the latter case the |
@bwoebi I wonder, why do you need these
This code works in PHP-7.4 and master |
@dstogov I ended up with a RC=1 reference from a VAR, this was just simplified to illustrate the problem, let's say:
Now, certainly |
But I generally need the addr trick to assign the contents directly to a variable, if it's a struct or array member, then the dim&prop write handlers handle it, but obviously |
I see. This probably may be fixed by the following patch diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c
index f91092ab03b..daca017f69f 100644
--- a/ext/ffi/ffi.c
+++ b/ext/ffi/ffi.c
@@ -4290,7 +4290,8 @@ ZEND_METHOD(FFI, addr) /* {{{ */
cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(cdata->type);
- if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1 && type->kind == ZEND_FFI_TYPE_POINTER) {
+ if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1 && type->kind == ZEND_FFI_TYPE_POINTER
+ && cdata->ptr_holder) {
zend_throw_error(zend_ffi_exception_ce, "FFI::addr() cannot create a reference to a temporary pointer");
RETURN_THROWS();
} @iluuu1994 what do you think? |
@dstogov Your patch doesn't fix Bobs example for me. TBH I'm not very familiar with FFI. I remember a use-after-free for a Symfony test that used FFI: $struct = $ffi->new('Foo');
$structPtrPtr = \FFI::addr(\FFI::addr($struct));
var_dump($structPtrPtr); I don't know how to fix Bobs test, so I'm ok with just reverting this change. FFI is inherently unsafe, so users are responsible for making sure the data survives. |
Are you sure? I just verified this again and it works. <?php
$f = FFI::cdef("typedef struct { char *bar; } other;");
class Container {
public $data;
function __construct($f) { $this->data = $f->new("other"); }
function &getBar() { return $this->data->bar; } // return by ref to get CData instead of null
}
$container = new Container($f);
$data = $f->new("char[2]");
$data[0] = "1";
FFI::addr($container->getBar())[0] = $f->cast("char*", $data); // directly write it
var_dump($container);
My patch doesn't affect |
I see, this doesn't fix the other case:
|
🤔 Yes, it fails for the code you just posted below for me.
I didn't even try this one. |
@bwoebi will the patch posted above help you? Or it's not enough anyway. |
@dstogov Yes, this helps me. The other case just happened as part of the reproducer. |
Well, that patch still doesn't work for me ^^ <?php
$f = FFI::cdef("typedef struct { char *bar; } other;");
class Container {
public $data;
function __construct($f) { $this->data = $f->new("other"); }
function &getBar() { return $this->data->bar; } // return by ref to get CData instead of null
}
$container = new Container($f);
$data = $f->new("char[2]");
$data[0] = "1";
FFI::addr($container->getBar())[0] = $f->cast("char*", $data); // directly write it
var_dump($container);
?>
I tried on PHP-8.3 and master. But if it works for you then great 🤷♂️ 😄 |
No, I meant, it's enough if this fixes the latter case. I can test later. |
* PHP-8.3: Fixed regression introduced by #9601
@bwoebi the problem should be fixed now |
Alex Dowad (1): Return value of mb_get_info can be NULL Ben Ramsey (1): Merge changes to CertificateGenerator.inc from PHP-8.2 Bob Weinand (2): Add NEWS entry for GH-12768 USE_ZEND_ALLOC=1 in tests with zend_test.observe_opline_in_zendmm=1 Derick Rethans (2): Import timelib 2022.10 Update NEWS Dmitry Stogov (7): Fixed GH-12747: Function JIT returns invalid error message for coalesce operator on invalid offset Fixed GH-12748: Function JIT emits "could not convert to int" warning at the same time as invalid offset Error Fixed regression introduced by php/php-src#9601 Fixed GH-12748: Function JIT emits "could not convert to int" warning at the same time as invalid offset Error Fixed GH-12812: Integer string in variable used as offset produces wrong undefined array key warning (#12817) Fixed GH-8251: Narrowing occurred during type inference of ZEND_FETCH_DIM_W Fixed type inference Florian Engelhardt (1): Fix invalid opline in OOM handlers within ZEND_FUNC_GET_ARGS and ZEND_BIND_STATIC (#12768) Gina Peter Banyard (5): ext/standard: Fix GH-9316 Mention correct bug number jit: fixed "Uninitialized string offset" warning being emitted at the same time as invalid offset Error jit: fixed JIT "Attempt to assign property of non-object" warning emitted at the same time as Error is being thrown Align error messages between normal VM and JIT for RW when using object as array (#12799) Ilija Tovilo (16): Fix undeclared variable in stat tests Disable -fsanitize=function on Clang 17 Fix astat imperciseness excemption in test [skip ci] Further increase allowable atime deviation Automatically mark tests as flaky Fix file test race condition Retry tests on deadlock [skip ci] Fix more test tmp file conflicts Temporarily disable failing zlib tests on travis (#10738) Fix use-after-free of name in var-var with malicious error handler Fix in-place modification of filename in php_message_handler_for_zend Reduce parallelism on frequently crashing jobs [skip ci] Skip resource intensive tidy test on GA Fix travis_wait Fix leak of call->extra_named_params on internal __call Fix compilation of ftp without openssl Jakub Zelenka (5): PHP 8.3 is now 8.3.1-dev Skip slow tests on Travis Fix bug #79945: Stream wrappers in imagecreatefrompng causes segfault Fix stream fclose_stdiocast_flush_in_progress type Fix #50713: openssl_pkcs7_verify() may ignore untrusted CAs Levi Morrison (1): test: allow other zend extensions to not fail the test Mikhail Galanin (1): Avoid using uninitialised struct Muhammad Moinur Rahman (2): Add host_cpu type for FreeBSD Add host_cpu type for FreeBSD Máté Kocsis (1): Fix the default value of $fetchMode in PDO::pgsqlGetNotify() Niels Dossche (15): Fix GH-12635: Test bug69398.phpt fails with ICU 74.1 Fix GH-12621: browscap segmentation fault when configured in the vhost Fix GH-12655: proc_open() does not take into account references in the descriptor array Fix GH-12675: MEMORY_LEAK in phpdbg_prompt.c Use __DIR__-relative path in tests Fix GH-12702: libxml2 2.12.0 issue building from src Fix GH-12616: DOM: Removing XMLNS namespace node results in invalid default: prefix Fix GH-12791: Possible dereference of NULL in MySQLnd debug code Test fixes for libxml2 2.12.0 Add missing NULL pointer checks related to the previous call frame Add missing NULL checks for spl autoload table Fix GH-12838: [SOAP] Temporary WSDL cache files not being deleted Fix GH-12826: Weird pointers issue in nested loops Fix libxml2 2.12 build due to API breaks Fix GH-9348: FTP & SSL session reuse Patrick Allaert (1): PHP-8.1 is now for PHP 8.1.28-dev Patrick Prasse (1): Fix bug GH-12705: Segmentation fault in fpm_status_export_to_zval Pierrick Charron (1): Prepare PHP 8.3.1 Remi Collet (1): zip: use index to avoid search by name ddv (1): Fix phpGH-12763: PGSQL pg_untrace(): Argument #1 ($connection) must be of type resource or null, PgSql\Connection given.
We're only disallowing pointers, as they always rely not only on the data but also the
CData.ptr_holder
to be there. Other unowned structures should be allowed, like structs, even ifrefcount == 1
. Targetingmaster
as this is an improvement rather than a bugfix.