Skip to content

Commit 0f3ca15

Browse files
committed
FFI: Perform bitfield operations byte-wise
Otherwise we may perform reads/writes outside the allocation, as already happens in 032.phpt.
1 parent 42b22d3 commit 0f3ca15

File tree

1 file changed

+80
-34
lines changed

1 file changed

+80
-34
lines changed

ext/ffi/ffi.c

Lines changed: 80 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -531,58 +531,104 @@ static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, voi
531531
}
532532
/* }}} */
533533

534-
static void zend_ffi_bit_field_to_zval(void *ptr, zend_ffi_field *field, zval *rv) /* {{{ */
534+
static uint64_t zend_ffi_bit_field_read(void *ptr, zend_ffi_field *field) /* {{{ */
535535
{
536-
uint64_t *p1 = (uint64_t *)((char*)ptr + (field->first_bit / 64) * 8);
537-
uint64_t *p2 = (uint64_t *)((char*)ptr + ((field->first_bit + field->bits - 1) / 64) * 8);
538-
uint64_t pos = field->first_bit % 64;
539-
uint64_t shift = 64 - (field->bits % 64);
540-
uint64_t val;
536+
size_t bit = field->first_bit;
537+
size_t last_bit = bit + field->bits - 1;
538+
uint8_t *p = (uint8_t *) ptr + bit / 8;
539+
uint8_t *last_p = (uint8_t *) ptr + last_bit / 8;
540+
size_t pos = bit % 8;
541+
size_t insert_pos = 0;
542+
uint8_t mask;
543+
uint64_t val = 0;
541544

542-
if (p1 == p2) {
543-
if (field->bits == 64) {
544-
val = *p1;
545-
shift = 0;
546-
} else {
547-
val = *p1 << (shift - pos);
548-
}
549-
} else {
550-
val = (*p1 >> pos) | (*p2 << (64 - pos));
545+
/* Bitfield fits into a single byte */
546+
if (p == last_p) {
547+
mask = (1U << field->bits) - 1U;
548+
return (*p >> pos) & mask;
549+
}
550+
551+
/* Read partial prefix byte */
552+
if (pos != 0) {
553+
size_t num_bits = 8 - pos;
554+
mask = ((1U << num_bits) - 1U) << pos;
555+
val = (*p++ >> pos) & mask;
556+
insert_pos += num_bits;
557+
}
558+
559+
/* Read full bytes */
560+
while (p < last_p) {
561+
val |= *p++ << insert_pos;
562+
insert_pos += 8;
563+
}
564+
565+
/* Read partial suffix byte */
566+
if (p == last_p) {
567+
size_t num_bits = last_bit % 8 + 1;
568+
mask = (1U << num_bits) - 1U;
569+
val |= (*p & mask) << insert_pos;
551570
}
571+
572+
return val;
573+
}
574+
575+
static void zend_ffi_bit_field_to_zval(void *ptr, zend_ffi_field *field, zval *rv) /* {{{ */
576+
{
577+
uint64_t val = zend_ffi_bit_field_read(ptr, field);
552578
if (ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_CHAR
553579
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT8
554580
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT16
555581
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT32
556582
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT64) {
557-
val = (int64_t)val >> shift;
558-
} else {
559-
val = val >> shift;
583+
/* Sign extend */
584+
uint64_t shift = 64 - (field->bits % 64);
585+
if (shift != 0) {
586+
val = (int64_t)(val << shift) >> shift;
587+
}
560588
}
561589
ZVAL_LONG(rv, val);
562590
}
563591
/* }}} */
564592

565593
static int zend_ffi_zval_to_bit_field(void *ptr, zend_ffi_field *field, zval *value) /* {{{ */
566594
{
567-
uint64_t *p1 = (uint64_t *)((char*)ptr + (field->first_bit / 64) * 8);
568-
uint64_t *p2 = (uint64_t *)((char*)ptr + ((field->first_bit + field->bits - 1) / (8 * 8)) * 8);
569-
uint64_t pos = field->first_bit % 64;
570-
uint64_t mask;
571595
uint64_t val = zval_get_long(value);
596+
size_t bit = field->first_bit;
597+
size_t last_bit = bit + field->bits - 1;
598+
uint8_t *p = (uint8_t *) ptr + bit / 8;
599+
uint8_t *last_p = (uint8_t *) ptr + last_bit / 8;
600+
size_t pos = bit % 8;
601+
uint8_t mask;
602+
603+
/* Bitfield fits into a single byte */
604+
if (p == last_p) {
605+
mask = ((1U << field->bits) - 1U) << pos;
606+
*p = (*p & ~mask) | ((val << pos) & mask);
607+
return SUCCESS;
608+
}
572609

573-
if (p1 == p2) {
574-
if (field->bits == 64) {
575-
*p1 = val;
576-
} else {
577-
mask = ((1ULL << field->bits) - 1ULL) << pos;
578-
*p1 = (*p1 & ~mask) | ((val << pos) & mask);
579-
}
580-
} else {
581-
mask = ((1ULL << (64 - pos)) - 1ULL) << pos;
582-
*p1 = (*p1 & ~mask) | ((val << pos) & mask);
583-
mask = (1ULL << pos) - 1ULL;
584-
*p2 = (*p2 & ~mask) | ((val >> (64 - pos)) & mask);
610+
/* Write partial prefix byte */
611+
if (pos != 0) {
612+
size_t num_bits = 8 - pos;
613+
mask = ((1U << num_bits) - 1U) << pos;
614+
*p = (*p & ~mask) | ((val << pos) & mask);
615+
p++;
616+
val >>= num_bits;
617+
}
618+
619+
/* Write full bytes */
620+
while (p < last_p) {
621+
*p++ = val;
622+
val >>= 8;
585623
}
624+
625+
/* Write partial suffix byte */
626+
if (p == last_p) {
627+
size_t num_bits = last_bit % 8 + 1;
628+
mask = (1U << num_bits) - 1U;
629+
*p = (*p & ~mask) | (val & mask);
630+
}
631+
586632
return SUCCESS;
587633
}
588634
/* }}} */

0 commit comments

Comments
 (0)