From a49e3abb21d73584553d7df30344293fc0b81cd0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Jan 2023 16:19:06 +0100 Subject: [PATCH] Fix incorrect bitshifting and masking in ffi bitfield When a uint8_t is bitshifted to the left, it is actually promoted to an int. For the current code this has the effect of a wrong sign-extension, and the result will also wrongly become zero when insert_pos >= 32. Fix this by adding an explicit cast. Furthermore, the partial prefix byte mask was computed incorrectly: the byte is already shifted so the mask should not account for the shift. --- ext/ffi/ffi.c | 6 +++--- ext/ffi/tests/gh10403.phpt | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 ext/ffi/tests/gh10403.phpt diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 686db459eff5b..107d2862c1256 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -592,14 +592,14 @@ static uint64_t zend_ffi_bit_field_read(void *ptr, zend_ffi_field *field) /* {{{ /* Read partial prefix byte */ if (pos != 0) { size_t num_bits = 8 - pos; - mask = ((1U << num_bits) - 1U) << pos; + mask = (1U << num_bits) - 1U; val = (*p++ >> pos) & mask; insert_pos += num_bits; } /* Read full bytes */ while (p < last_p) { - val |= *p++ << insert_pos; + val |= (uint64_t) *p++ << insert_pos; insert_pos += 8; } @@ -607,7 +607,7 @@ static uint64_t zend_ffi_bit_field_read(void *ptr, zend_ffi_field *field) /* {{{ if (p == last_p) { size_t num_bits = last_bit % 8 + 1; mask = (1U << num_bits) - 1U; - val |= (*p & mask) << insert_pos; + val |= (uint64_t) (*p & mask) << insert_pos; } return val; diff --git a/ext/ffi/tests/gh10403.phpt b/ext/ffi/tests/gh10403.phpt new file mode 100644 index 0000000000000..6e422c438fdf6 --- /dev/null +++ b/ext/ffi/tests/gh10403.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-10403: Fix incorrect bitshifting and masking in ffi bitfield +--EXTENSIONS-- +ffi +--SKIPIF-- + +--FILE-- +new('struct MyStruct'); +$test_struct->x = 1023; +$test_values = [0x3fafbfcfdfefff, 0x01020304050607, 0, 0x3fffffffffffff, 0x2ffffffffffff5]; +foreach ($test_values as $test_value) { + $test_struct->y = $test_value; + var_dump($test_struct->y === $test_value); +} +var_dump($test_struct->x); +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +int(1023)