1
Fork 0

rustc_serialize: fix incorrect signed LEB128 decoding

The signed LEB128 decoding function used a hardcoded constant of 64
instead of the number of bits in the type of integer being decoded,
which resulted in incorrect results for some inputs. Fix this, make the
decoding more consistent with the unsigned version, and increase the
LEB128 encoding and decoding test coverage.
This commit is contained in:
Tyson Nottingham 2020-12-29 15:14:33 -08:00
parent 52f21791fb
commit f15fae822e
4 changed files with 88 additions and 57 deletions

View file

@ -119,28 +119,38 @@ impl_write_signed_leb128!(write_i64_leb128, i64);
impl_write_signed_leb128!(write_i128_leb128, i128); impl_write_signed_leb128!(write_i128_leb128, i128);
impl_write_signed_leb128!(write_isize_leb128, isize); impl_write_signed_leb128!(write_isize_leb128, isize);
#[inline] macro_rules! impl_read_signed_leb128 {
pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) { ($fn_name:ident, $int_ty:ty) => {
let mut result = 0; #[inline]
let mut shift = 0; pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
let mut position = start_position; let mut result = 0;
let mut byte; let mut shift = 0;
let mut position = 0;
let mut byte;
loop { loop {
byte = data[position]; byte = slice[position];
position += 1; position += 1;
result |= i128::from(byte & 0x7F) << shift; result |= <$int_ty>::from(byte & 0x7F) << shift;
shift += 7; shift += 7;
if (byte & 0x80) == 0 { if (byte & 0x80) == 0 {
break; break;
}
}
if (shift < <$int_ty>::BITS) && ((byte & 0x40) != 0) {
// sign extend
result |= (!0 << shift);
}
(result, position)
} }
} };
if (shift < 64) && ((byte & 0x40) != 0) {
// sign extend
result |= -(1 << shift);
}
(result, position - start_position)
} }
impl_read_signed_leb128!(read_i16_leb128, i16);
impl_read_signed_leb128!(read_i32_leb128, i32);
impl_read_signed_leb128!(read_i64_leb128, i64);
impl_read_signed_leb128!(read_i128_leb128, i128);
impl_read_signed_leb128!(read_isize_leb128, isize);

View file

@ -17,6 +17,7 @@ Core encoding and decoding interfaces.
#![feature(min_specialization)] #![feature(min_specialization)]
#![feature(vec_spare_capacity)] #![feature(vec_spare_capacity)]
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
#![feature(int_bits_const)]
#![feature(maybe_uninit_slice)] #![feature(maybe_uninit_slice)]
#![feature(new_uninit)] #![feature(new_uninit)]
#![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(test))]

View file

@ -1,4 +1,4 @@
use crate::leb128::{self, max_leb128_len, read_signed_leb128}; use crate::leb128::{self, max_leb128_len};
use crate::serialize; use crate::serialize;
use std::borrow::Cow; use std::borrow::Cow;
use std::fs::File; use std::fs::File;
@ -561,7 +561,7 @@ impl<'a> Decoder<'a> {
} }
} }
macro_rules! read_uleb128 { macro_rules! read_leb128 {
($dec:expr, $fun:ident) => {{ ($dec:expr, $fun:ident) => {{
let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]); let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]);
$dec.position += bytes_read; $dec.position += bytes_read;
@ -569,14 +569,6 @@ macro_rules! read_uleb128 {
}}; }};
} }
macro_rules! read_sleb128 {
($dec:expr, $t:ty) => {{
let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position);
$dec.position += bytes_read;
Ok(value as $t)
}};
}
impl<'a> serialize::Decoder for Decoder<'a> { impl<'a> serialize::Decoder for Decoder<'a> {
type Error = String; type Error = String;
@ -587,22 +579,22 @@ impl<'a> serialize::Decoder for Decoder<'a> {
#[inline] #[inline]
fn read_u128(&mut self) -> Result<u128, Self::Error> { fn read_u128(&mut self) -> Result<u128, Self::Error> {
read_uleb128!(self, read_u128_leb128) read_leb128!(self, read_u128_leb128)
} }
#[inline] #[inline]
fn read_u64(&mut self) -> Result<u64, Self::Error> { fn read_u64(&mut self) -> Result<u64, Self::Error> {
read_uleb128!(self, read_u64_leb128) read_leb128!(self, read_u64_leb128)
} }
#[inline] #[inline]
fn read_u32(&mut self) -> Result<u32, Self::Error> { fn read_u32(&mut self) -> Result<u32, Self::Error> {
read_uleb128!(self, read_u32_leb128) read_leb128!(self, read_u32_leb128)
} }
#[inline] #[inline]
fn read_u16(&mut self) -> Result<u16, Self::Error> { fn read_u16(&mut self) -> Result<u16, Self::Error> {
read_uleb128!(self, read_u16_leb128) read_leb128!(self, read_u16_leb128)
} }
#[inline] #[inline]
@ -614,27 +606,27 @@ impl<'a> serialize::Decoder for Decoder<'a> {
#[inline] #[inline]
fn read_usize(&mut self) -> Result<usize, Self::Error> { fn read_usize(&mut self) -> Result<usize, Self::Error> {
read_uleb128!(self, read_usize_leb128) read_leb128!(self, read_usize_leb128)
} }
#[inline] #[inline]
fn read_i128(&mut self) -> Result<i128, Self::Error> { fn read_i128(&mut self) -> Result<i128, Self::Error> {
read_sleb128!(self, i128) read_leb128!(self, read_i128_leb128)
} }
#[inline] #[inline]
fn read_i64(&mut self) -> Result<i64, Self::Error> { fn read_i64(&mut self) -> Result<i64, Self::Error> {
read_sleb128!(self, i64) read_leb128!(self, read_i64_leb128)
} }
#[inline] #[inline]
fn read_i32(&mut self) -> Result<i32, Self::Error> { fn read_i32(&mut self) -> Result<i32, Self::Error> {
read_sleb128!(self, i32) read_leb128!(self, read_i32_leb128)
} }
#[inline] #[inline]
fn read_i16(&mut self) -> Result<i16, Self::Error> { fn read_i16(&mut self) -> Result<i16, Self::Error> {
read_sleb128!(self, i16) read_leb128!(self, read_i16_leb128)
} }
#[inline] #[inline]
@ -646,7 +638,7 @@ impl<'a> serialize::Decoder for Decoder<'a> {
#[inline] #[inline]
fn read_isize(&mut self) -> Result<isize, Self::Error> { fn read_isize(&mut self) -> Result<isize, Self::Error> {
read_sleb128!(self, isize) read_leb128!(self, read_isize_leb128)
} }
#[inline] #[inline]

View file

@ -1,3 +1,4 @@
#![feature(int_bits_const)]
#![feature(maybe_uninit_slice)] #![feature(maybe_uninit_slice)]
#![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array)]
@ -8,16 +9,28 @@ macro_rules! impl_test_unsigned_leb128 {
($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => { ($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
#[test] #[test]
fn $test_name() { fn $test_name() {
// Test 256 evenly spaced values of integer range,
// integer max value, and some "random" numbers.
let mut values = Vec::new();
let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
values.extend((0..256).map(|i| $int_ty::MIN + i * increment));
values.push($int_ty::MAX);
values.extend(
(-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFu64 as $int_ty)),
);
let mut stream = Vec::new(); let mut stream = Vec::new();
for x in 0..62 { for &x in &values {
let mut buf = MaybeUninit::uninit_array(); let mut buf = MaybeUninit::uninit_array();
stream.extend($write_fn_name(&mut buf, (3u64 << x) as $int_ty)); stream.extend($write_fn_name(&mut buf, x));
} }
let mut position = 0; let mut position = 0;
for x in 0..62 { for &expected in &values {
let expected = (3u64 << x) as $int_ty;
let (actual, bytes_read) = $read_fn_name(&stream[position..]); let (actual, bytes_read) = $read_fn_name(&stream[position..]);
assert_eq!(expected, actual); assert_eq!(expected, actual);
position += bytes_read; position += bytes_read;
@ -34,12 +47,28 @@ impl_test_unsigned_leb128!(test_u128_leb128, write_u128_leb128, read_u128_leb128
impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize); impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize);
macro_rules! impl_test_signed_leb128 { macro_rules! impl_test_signed_leb128 {
($test_name:ident, $write_fn_name:ident, $int_ty:ident) => { ($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
#[test] #[test]
fn $test_name() { fn $test_name() {
let values: Vec<_> = (-500..500) // Test 256 evenly spaced values of integer range,
.map(|i| ((i as $int_ty).wrapping_mul(0x12345789ABCDEF_i64 as $int_ty))) // integer max value, and some "random" numbers.
.collect(); let mut values = Vec::new();
let mut value = $int_ty::MIN;
let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
for _ in 0..256 {
values.push(value);
// The addition in the last loop iteration overflows.
value = value.wrapping_add(increment);
}
values.push($int_ty::MAX);
values.extend(
(-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFi64 as $int_ty)),
);
let mut stream = Vec::new(); let mut stream = Vec::new();
for &x in &values { for &x in &values {
@ -48,9 +77,8 @@ macro_rules! impl_test_signed_leb128 {
} }
let mut position = 0; let mut position = 0;
for &x in &values { for &expected in &values {
let expected = x as i128; let (actual, bytes_read) = $read_fn_name(&stream[position..]);
let (actual, bytes_read) = read_signed_leb128(&mut stream, position);
assert_eq!(expected, actual); assert_eq!(expected, actual);
position += bytes_read; position += bytes_read;
} }
@ -59,8 +87,8 @@ macro_rules! impl_test_signed_leb128 {
}; };
} }
impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, i16); impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, read_i16_leb128, i16);
impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, i32); impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, read_i32_leb128, i32);
impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, i64); impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, read_i64_leb128, i64);
impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, i128); impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, read_i128_leb128, i128);
impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, isize); impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, read_isize_leb128, isize);