php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79319 FFI::cast() from void* to intptr_t crashes PHP
Submitted: 2020-02-28 21:52 UTC Modified: 2021-09-16 21:25 UTC
From: jprofic at yandex dot ru Assigned:
Status: Open Package: FFI (PECL)
PHP Version: 7.4.3 OS: Windows
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: jprofic at yandex dot ru
New email:
PHP Version: OS:

 

 [2020-02-28 21:52 UTC] jprofic at yandex dot ru
Description:
------------
There is no way to FFI::cast() an opaque void* pointer to any scalar type (like intptr_t) so that its value can be compared to some constant (e.g. HANDLE returned by Win32 CreateFileW() call and INVALID_POINTER_VALUE = -1). Everything I tried just crashed PHP.
There are workarounds. Doing FFI::cast('void*', -1) and comparing them both as void* seems to work (“seems” because I haven't managed to subtract two pointers, but it's another tale), however it can't be a class constant. Also using FFI::memcpy(FFI:new('intptr_t'), FFI::addr(FFICData:void*))) seems to work, but the additional FFI::addr() call is required to prevent PHP from crashing as well.

Test script:
---------------
php -r "var_dump(ffi::cast('intptr_t', ffi::cast('void*', -1)));"

Expected result:
----------------
No crash and output similar to:
object(FFICData:int64_t)#1 (1) {
  ["cdata"]=>
  int(-1)
}

Actual result:
--------------
Crash and no output.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-03-02 08:02 UTC] cmb@php.net
Is there any particular reason why you don't use an intprt_t
in the first place?

    ffi::cast('intptr_t', -1)
 [2020-03-02 08:28 UTC] jprofic at yandex dot ru
I can't declare HANDLE as intptr_t because there are Win32 API calls that treat HANDLE as real pointers (LocalFree() comes to mind).
If you meant my example, then the "ffi::cast('void*', -1)" part is emulating what I'm getting from for example CreateFileW() on error. And the rest is the culprit in the reason of this bug report.
Moreover I thinks it's kinda dangerous to automatically dereference any pointer, especially the void* ones: http://git.php.net/?p=php-src.git;a=blob;f=ext/ffi/ffi.c;h=0696512342ea2ad28ca0c7b3cbdb12c87baded6e;hb=HEAD#l3861
 [2020-03-03 09:08 UTC] cmb@php.net
> Moreover I thinks it's kinda dangerous to automatically
> dereference any pointer, especially the void* ones: […]

Yes, it is; that's why it is not done at the place you've linked
to.  The supplied test script actually crashes when var_dump()ing
the intptr_t, because -1 is dereferenced[1].  It may make sense to
add special support for intptr_t (currently this is just treated
as int32_t/int64_t[2]).  On the other hand, just sticking with
void* should also work for you (at least regarding equality
comparison; pointer arithmetic on void* is indeed another story).

[1] <https://github.com/php/php-src/blob/php-7.4.3/ext/ffi/ffi.c#L497>
[2] <https://github.com/php/php-src/blob/php-7.4.3/ext/ffi/ffi.c#L5205-L5217>
 [2020-03-03 10:00 UTC] jprofic at yandex dot ru
Am I reading C wrong then? Doesn't “*(void**)ptr” mean cast “ptr” to “(void**)” and then dereference it to “void*”? Or is the storage method in ffi slightly more complicated than it looks at the first glance?

Hm, var_dump()'ing ffi::cast('void*', -1) produces strange and unintuitive result:
object(FFI\CData:void*)#1 (1) {
  [0]=>
  int(-1)
}
And var_dump()'ing ffi::cast('void*', -1)[0] is even funnier:
object(FFI\CData:void)#2 (1) {
  ["cdata"]=>
  *RECURSION*
}
Although I think I'm starting to get why it is so.

Yes, I'm currently comparing two ffi\CData:void* objects and it works. Still I would prefer to have the same behaviour as present in C in regard of casting and other pointer usage to prevent any possible misunderstanding, just keeping in mind that ffi\CData object itself is a kind of pointer. Also I don't think that having a special case for intptr_t will help as at first I was trying all types of integers (and was surprised that wchar_t wasn't supported).

To sum it up, I still don't think PHP should crash in my test script, and maybe there should be less magic involved in handling pointers. What I mean is when ffi\CData is of a pointer type, then   maybe it's alright to dereference it in var_dump() and friends if it's of a known type to help with debugging. But properties-wise, I think it should have the only property like “cdata” but for pointers, named for example “intptr” (also maybe readonly).
 [2021-09-16 21:25 UTC] cmb@php.net
-Package: ffi +Package: FFI
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Sun Oct 24 16:03:34 2021 UTC