Skip to content

Commit 560ca9c

Browse files
authored
Fix incorrect bitshifting and masking in ffi bitfield (#10403)
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.
1 parent 974dba3 commit 560ca9c

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

ext/ffi/ffi.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -592,22 +592,22 @@ static uint64_t zend_ffi_bit_field_read(void *ptr, zend_ffi_field *field) /* {{{
592592
/* Read partial prefix byte */
593593
if (pos != 0) {
594594
size_t num_bits = 8 - pos;
595-
mask = ((1U << num_bits) - 1U) << pos;
595+
mask = (1U << num_bits) - 1U;
596596
val = (*p++ >> pos) & mask;
597597
insert_pos += num_bits;
598598
}
599599

600600
/* Read full bytes */
601601
while (p < last_p) {
602-
val |= *p++ << insert_pos;
602+
val |= (uint64_t) *p++ << insert_pos;
603603
insert_pos += 8;
604604
}
605605

606606
/* Read partial suffix byte */
607607
if (p == last_p) {
608608
size_t num_bits = last_bit % 8 + 1;
609609
mask = (1U << num_bits) - 1U;
610-
val |= (*p & mask) << insert_pos;
610+
val |= (uint64_t) (*p & mask) << insert_pos;
611611
}
612612

613613
return val;

ext/ffi/tests/gh10403.phpt

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-10403: Fix incorrect bitshifting and masking in ffi bitfield
3+
--EXTENSIONS--
4+
ffi
5+
--SKIPIF--
6+
<?php if (PHP_INT_SIZE != 8) echo "skip this test is for 64-bit only"; ?>
7+
--FILE--
8+
<?php
9+
$ffi = FFI::cdef(<<<EOF
10+
struct MyStruct {
11+
uint64_t x : 10;
12+
uint64_t y : 54;
13+
};
14+
EOF);
15+
16+
$test_struct = $ffi->new('struct MyStruct');
17+
$test_struct->x = 1023;
18+
$test_values = [0x3fafbfcfdfefff, 0x01020304050607, 0, 0x3fffffffffffff, 0x2ffffffffffff5];
19+
foreach ($test_values as $test_value) {
20+
$test_struct->y = $test_value;
21+
var_dump($test_struct->y === $test_value);
22+
}
23+
var_dump($test_struct->x);
24+
--EXPECT--
25+
bool(true)
26+
bool(true)
27+
bool(true)
28+
bool(true)
29+
bool(true)
30+
int(1023)

0 commit comments

Comments
 (0)