Skip to content
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

FFI::cast() from pointer to array is broken #7867

Closed
userqq opened this issue Jan 1, 2022 · 1 comment
Closed

FFI::cast() from pointer to array is broken #7867

userqq opened this issue Jan 1, 2022 · 1 comment

Comments

@userqq
Copy link

userqq commented Jan 1, 2022

Hi!
Not sure its incorrect behaviour, but as you can see FFI::cast when applied to FFI\CData with offset returns data different from copy result.

The following code:

$value = FFI::new('char[26]');
FFI::memcpy($value, implode('', range('a', 'z')), 26);

$slice = FFI::new('char[4]');

echo 'cast from start' . PHP_EOL;
FFI::memcpy($slice, $value, 4);
var_dump($value + 0, $slice, FFI::cast('char[4]', $value));
echo PHP_EOL;

echo 'cast with offset' . PHP_EOL;
FFI::memcpy($slice, $value + 4, 4);
var_dump($value + 4, $slice, FFI::cast('char[4]', $value + 4));
echo PHP_EOL;

Resulted in this output:

cast from start
object(FFI\CData:char*)#3 (1) {
  [0]=>
  string(1) "a"
}
object(FFI\CData:char[4])#2 (4) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [3]=>
  string(1) "d"
}
object(FFI\CData:char[4])#4 (4) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [3]=>
  string(1) "d"
}

cast with offset
object(FFI\CData:char*)#4 (1) {
  [0]=>
  string(1) "e"
}
object(FFI\CData:char[4])#2 (4) {
  [0]=>
  string(1) "e"
  [1]=>
  string(1) "f"
  [2]=>
  string(1) "g"
  [3]=>
  string(1) "h"
}
object(FFI\CData:char[4])#5 (4) {
  [0]=>
  string(1) "▒"
  [1]=>
  string(1) "C"
  [2]=>
  string(1) "▒"
  [3]=>
  string(1) "x"
}

But I expected this output instead:

cast from start
object(FFI\CData:char*)#3 (1) {
  [0]=>
  string(1) "a"
}
object(FFI\CData:char[4])#2 (4) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [3]=>
  string(1) "d"
}
object(FFI\CData:char[4])#4 (4) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
  [3]=>
  string(1) "d"
}

cast with offset
object(FFI\CData:char*)#4 (1) {
  [0]=>
  string(1) "e"
}
object(FFI\CData:char[4])#2 (4) {
  [0]=>
  string(1) "e"
  [1]=>
  string(1) "f"
  [2]=>
  string(1) "g"
  [3]=>
  string(1) "h"
}
object(FFI\CData:char[4])#5 (4) {
  [0]=>
  string(1) "e"
  [1]=>
  string(1) "f"
  [2]=>
  string(1) "g"
  [3]=>
  string(1) "h"
}

PHP Version

PHP 8.1.1

Operating System

No response

@cmb69
Copy link
Contributor

cmb69 commented Jan 3, 2022

There are issues regarding arrays and pointers of a certain type. While pointer arithmetic mostly works, it looses the alignment, e.g.

$value = FFI::new('char[26]');
var_dump(
    FFI::alignof($value),    // int(1)
    FFI::alignof($value + 0) // int(8)
);

But then there is the more serious issue, that casting from a pointer type to an array doesn't properly check the size of the types, e.g.

$value = FFI::new('char[26]');
var_dump(FFI::cast('char[4]', $value + 0));
var_dump(FFI::cast('char[9]', $value + 0));

outputs:

object(FFI\CData:char[4])#3 (4) {
  [0]=>
  string(1) ""
  [1]=>
  string(1) ""
  [2]=>
  string(1) "E"
  [3]=>
  string(1) ""
}

Fatal error: Uncaught FFI\Exception: attempt to cast to larger type in %s:5

cmb69 added a commit to cmb69/php-src that referenced this issue Jan 3, 2022
Casting from pointer to array is special, so we must not fall back to
the general FFI casting.  There is a particular issue regarding the
size comparison, namely that the pointer size is always 8 for 64bit
architectures, but the size of an array is determined by its
declaration, so as is casting a pointer to an array with more than 8
elements would fail, but casting to an array with less than 9 elements
succeeds, but the internal pointer would point to some arbitrary
memory.

We fix this by properly supporting the cast.  An alternative would be
to deny this kind of cast generally, since it is not necessarily safe.
However, FFI isn't necessarily safe anyway.
@cmb69 cmb69 changed the title FFI::cast vs FFI::memcpy FFI::cast() from pointer to array is broken Jan 3, 2022
@cmb69 cmb69 linked a pull request Jan 3, 2022 that will close this issue
@cmb69 cmb69 closed this as completed in 703cac3 Jan 10, 2022
cmb69 added a commit that referenced this issue Jan 10, 2022
* PHP-8.0:
  Fix GH-7867: FFI::cast() from pointer to array is broken
cmb69 added a commit that referenced this issue Jan 10, 2022
* PHP-8.1:
  Fix GH-7867: FFI::cast() from pointer to array is broken
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants
@cmb69 @userqq and others