# FFI extension / NULL-Checks

- Thread: https://externals.io/message/106910
- Original archive: https://news-web.php.net/php.internals/106910
- Started by Philip Hofstetter on 2019-09-09T06:03:32+00:00
- Messages: 5
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #106910 — Philip Hofstetter — 2019-09-09T06:03:32+00:00

URL: https://externals.io/message/106910#106910

Hello,

(I'm writing to the internals list because I don't think that at this
point, FFI usage is wide-spread enough to get an opinion on other venues)

maybe I'm just stupid, but I think there has been a slight oversight in the
API design for the FFI interface.

My problem is with functions that return a pointer to a struct as an out
parameter.

So in C, it would look like this:

Error* err;
func(&err);

if (err != NULL){
   do_error_handling();
}

If I translate this to PHP FFI, I would do

$err = $ffi->new("Error*");
$ffi->func(FFI::addr($err));

if I `var_dump` $err, I do see a public {0} property be set to `NULL` or to
actual error data.

My issue is though: How do I check whether err* as been assgined to?

isset($err)
$err[0] != null
-> is always true, regardless of whether func as assigned an error or not

isset($err->{0})
isset($err[0])
-> Cannot use object of type FFI\CData as array

$err->{0} != NULL;
-> FFI\Exception: `NULL` pointer dereference

$err[0];
-> segfault

I'm sure I must be doing something wrong, but I don't know what. Also, the
documentation could do with an example on how to achieve the desired result
and, TBH, I think some of the various attempts produce a somewhat
unexpected output.

Any comments?

Philip


var_dump(isset($r->{0}));
-> Error:  Cannot use object of type FFI\CData as array

$a = '{0}';
var_dump(isset($r->$a));
-> Error:  Cannot use object of type FFI\CData as array


    var_dump(isset($r[0]));
    var_dump($r[0]);

---

## #106915 — Stanislav Malyshev — 2019-09-09T08:13:21+00:00

URL: https://externals.io/message/106910#106915 · In reply to: #106910

Hi!

> isset($err)
> $err[0] != null
> -> is always true, regardless of whether func as assigned an error or not
> 
> $err[0];
> -> segfault

This is a bit confusing - if $err[0] segfaults, how it can be != null?
Does this behave differently in different contexts? Anyway, it shouldn't
probably segfault in any case, so this looks like a bug.

-- 
Stas Malyshev
smalyshev@gmail.com

---

## #106916 — Philip Hofstetter — 2019-09-09T09:27:06+00:00

URL: https://externals.io/message/106910#106916 · In reply to: #106915

On Mon, Sep 9, 2019 at 10:13 AM Stanislav Malyshev <smalyshev@gmail.com>
wrote:


> > […]
>
> This is a bit confusing - if $err[0] segfaults, how it can be != null?
> Does this behave differently in different contexts? Anyway, it shouldn't
> probably segfault in any case, so this looks like a bug.
>

I think there are a few disconnected issues. One is that checking something
for being `NULL` is a bit tricky with the given API and the other is that the
API around FFI\CData is a bit inconsistent and tricky to use.

Given

$err = $ffi->new("Error*");

what the library does is assume that $err is now a pointer to an array
(which is what C does I guess), but yet, there seems to be a bit of an
issue with actually accessing that array (if it's set to NULL) - and also,
don't use `var_dump()` to inspect it - that's deceptive because there's a
debug_info handler that knows ore than the actual field accessors (which
too makes this more complicated than needed).

So. Let's assume that in

$err = $ffi->new("Error*");
$ffi->func(FFI::addr($err));

func() sets that argument to NULL, then

$f = $err[0];
var_dump(gettype($f));

returns "object" - so the zeroth index exists (hence the isset being true).

However there's also
$f = $err->{0};

which normally would throw "Attempt to read undefined field '0' of C
struct/union" but because $err is seen as an array, accessing a property
named 0 is possible, but then a second null check will happen via
https://github.com/php/php-src/blob/09e9c4f3c11a875c67334578621084f156155307/ext/ffi/ffi.c#L1103-L1107
and
you get "NULL pointer dereference" instead.

I believe this difference between [0] and {0} shouldn't exist.

And even aside of that, I see no way in which user-code can ever check any
reference for `NULL` because any access of a Cdata property or index that's
happen to be set to `NULL` will either segfault, throw or fail a comparison
in PHP because Cdata instances, according to PHP's type conversion rules,
always convert to something that compares truthy when put in boolean
context.

The inconsistency between [] and {} aside, I believe there should be an
FFI::isNull() static method or a Cdata::isNull() instance method that allow
user-code to check things for being NULL. Or the throw in the code I liked
to above is removed, but that would leave the inconsistency between [] and
{}

Or maybe I'm just overlooking something and there's a simple way to check
something for being NULL, but if there is, it IMHO needs better docs.

Philip

---

## #106917 — Philip Hofstetter — 2019-09-09T11:33:14+00:00

URL: https://externals.io/message/106910#106917 · In reply to: #106910

In response to these findings so far, I have now submitted

https://github.com/php/php-src/pull/4691

that adds an FFI::isNull() method that returns true if the passed CData
instance points to NULL.

If you agree that something like this is indeed needed, I will gladly
update the PR and add unit tests.

Philip

On Mon, Sep 9, 2019 at 8:03 AM Philip Hofstetter <phofstetter@sensational.ch>
wrote:

> Hello,
>
> (I'm writing to the internals list because I don't think that at this
> point, FFI usage is wide-spread enough to get an opinion on other venues)
>
> maybe I'm just stupid, but I think there has been a slight oversight in
> the API design for the FFI interface.
>
> My problem is with functions that return a pointer to a struct as an out
> parameter.
>
> So in C, it would look like this:
>
> Error* err;
> func(&err);
>
> if (err != NULL){
>    do_error_handling();
> }
>
> If I translate this to PHP FFI, I would do
>
> $err = $ffi->new("Error*");
> $ffi->func(FFI::addr($err));
>
> if I `var_dump` $err, I do see a public {0} property be set to `NULL` or to
> actual error data.
>
> My issue is though: How do I check whether err* as been assgined to?
>
> isset($err)
> $err[0] != null
> -> is always true, regardless of whether func as assigned an error or not
>
> isset($err->{0})
> isset($err[0])
> -> Cannot use object of type FFI\CData as array
>
> $err->{0} != NULL;
> -> FFI\Exception: `NULL` pointer dereference
>
> $err[0];
> -> segfault
>
> I'm sure I must be doing something wrong, but I don't know what. Also, the
> documentation could do with an example on how to achieve the desired result
> and, TBH, I think some of the various attempts produce a somewhat
> unexpected output.
>
> Any comments?
>
> Philip
>
>
> var_dump(isset($r->{0}));
> -> Error:  Cannot use object of type FFI\CData as array
>
> $a = '{0}';
> var_dump(isset($r->$a));
> -> Error:  Cannot use object of type FFI\CData as array
>
>
>     var_dump(isset($r[0]));
>     var_dump($r[0]);
>
>
>

-- 
Sensational AG
Giesshübelstrasse 62c, Postfach 1966, 8021 Zürich
Tel. +41 43 544 09 60, Mobile  +41 79 341 01 99
info@sensational.ch, http://www.sensational.ch

---

## #106918 — Christoph M. Becker — 2019-09-09T12:15:21+00:00

URL: https://externals.io/message/106910#106918 · In reply to: #106910

> (I'm writing to the internals list because I don't think that at this
> point, FFI usage is wide-spread enough to get an opinion on other venues)
>
> maybe I'm just stupid, but I think there has been a slight oversight in the
> API design for the FFI interface.
>
> My problem is with functions that return a pointer to a struct as an out
> parameter.
>
> So in C, it would look like this:
>
> Error* err;
> func(&err);
>
> if (err != NULL){
>    do_error_handling();
> }
>
> If I translate this to PHP FFI, I would do
>
> $err = $ffi->new("Error*");
> $ffi->func(FFI::addr($err));
>
> if I `var_dump` $err, I do see a public {0} property be set to `NULL` or to
> actual error data.
>
> My issue is though: How do I check whether err* as been assgined to?

You could check FFI::addr($err)[0]:

  $err = $ffi->new("Error*");
  $perr = FFI::addr($err);
  $ffi->func($perr);
  var_dump(is_null($perr[0]));
  FFI::free($perr);

Not sure if it's supposed to require this indirection, though.

--
Christoph M. Becker
