Skip to content

Commit f15fae8

Browse files
committed
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.
1 parent 52f2179 commit f15fae8

File tree

4 files changed

+89
-58
lines changed

4 files changed

+89
-58
lines changed

compiler/rustc_serialize/src/leb128.rs

+32-22
Original file line numberDiff line numberDiff line change
@@ -119,28 +119,38 @@ impl_write_signed_leb128!(write_i64_leb128, i64);
119119
impl_write_signed_leb128!(write_i128_leb128, i128);
120120
impl_write_signed_leb128!(write_isize_leb128, isize);
121121

122-
#[inline]
123-
pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
124-
let mut result = 0;
125-
let mut shift = 0;
126-
let mut position = start_position;
127-
let mut byte;
128-
129-
loop {
130-
byte = data[position];
131-
position += 1;
132-
result |= i128::from(byte & 0x7F) << shift;
133-
shift += 7;
134-
135-
if (byte & 0x80) == 0 {
136-
break;
137-
}
138-
}
122+
macro_rules! impl_read_signed_leb128 {
123+
($fn_name:ident, $int_ty:ty) => {
124+
#[inline]
125+
pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
126+
let mut result = 0;
127+
let mut shift = 0;
128+
let mut position = 0;
129+
let mut byte;
130+
131+
loop {
132+
byte = slice[position];
133+
position += 1;
134+
result |= <$int_ty>::from(byte & 0x7F) << shift;
135+
shift += 7;
136+
137+
if (byte & 0x80) == 0 {
138+
break;
139+
}
140+
}
139141

140-
if (shift < 64) && ((byte & 0x40) != 0) {
141-
// sign extend
142-
result |= -(1 << shift);
143-
}
142+
if (shift < <$int_ty>::BITS) && ((byte & 0x40) != 0) {
143+
// sign extend
144+
result |= (!0 << shift);
145+
}
144146

145-
(result, position - start_position)
147+
(result, position)
148+
}
149+
};
146150
}
151+
152+
impl_read_signed_leb128!(read_i16_leb128, i16);
153+
impl_read_signed_leb128!(read_i32_leb128, i32);
154+
impl_read_signed_leb128!(read_i64_leb128, i64);
155+
impl_read_signed_leb128!(read_i128_leb128, i128);
156+
impl_read_signed_leb128!(read_isize_leb128, isize);

compiler/rustc_serialize/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Core encoding and decoding interfaces.
1717
#![feature(min_specialization)]
1818
#![feature(vec_spare_capacity)]
1919
#![feature(core_intrinsics)]
20+
#![feature(int_bits_const)]
2021
#![feature(maybe_uninit_slice)]
2122
#![feature(new_uninit)]
2223
#![cfg_attr(test, feature(test))]

compiler/rustc_serialize/src/opaque.rs

+12-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::leb128::{self, max_leb128_len, read_signed_leb128};
1+
use crate::leb128::{self, max_leb128_len};
22
use crate::serialize;
33
use std::borrow::Cow;
44
use std::fs::File;
@@ -561,22 +561,14 @@ impl<'a> Decoder<'a> {
561561
}
562562
}
563563

564-
macro_rules! read_uleb128 {
564+
macro_rules! read_leb128 {
565565
($dec:expr, $fun:ident) => {{
566566
let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]);
567567
$dec.position += bytes_read;
568568
Ok(value)
569569
}};
570570
}
571571

572-
macro_rules! read_sleb128 {
573-
($dec:expr, $t:ty) => {{
574-
let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position);
575-
$dec.position += bytes_read;
576-
Ok(value as $t)
577-
}};
578-
}
579-
580572
impl<'a> serialize::Decoder for Decoder<'a> {
581573
type Error = String;
582574

@@ -587,22 +579,22 @@ impl<'a> serialize::Decoder for Decoder<'a> {
587579

588580
#[inline]
589581
fn read_u128(&mut self) -> Result<u128, Self::Error> {
590-
read_uleb128!(self, read_u128_leb128)
582+
read_leb128!(self, read_u128_leb128)
591583
}
592584

593585
#[inline]
594586
fn read_u64(&mut self) -> Result<u64, Self::Error> {
595-
read_uleb128!(self, read_u64_leb128)
587+
read_leb128!(self, read_u64_leb128)
596588
}
597589

598590
#[inline]
599591
fn read_u32(&mut self) -> Result<u32, Self::Error> {
600-
read_uleb128!(self, read_u32_leb128)
592+
read_leb128!(self, read_u32_leb128)
601593
}
602594

603595
#[inline]
604596
fn read_u16(&mut self) -> Result<u16, Self::Error> {
605-
read_uleb128!(self, read_u16_leb128)
597+
read_leb128!(self, read_u16_leb128)
606598
}
607599

608600
#[inline]
@@ -614,27 +606,27 @@ impl<'a> serialize::Decoder for Decoder<'a> {
614606

615607
#[inline]
616608
fn read_usize(&mut self) -> Result<usize, Self::Error> {
617-
read_uleb128!(self, read_usize_leb128)
609+
read_leb128!(self, read_usize_leb128)
618610
}
619611

620612
#[inline]
621613
fn read_i128(&mut self) -> Result<i128, Self::Error> {
622-
read_sleb128!(self, i128)
614+
read_leb128!(self, read_i128_leb128)
623615
}
624616

625617
#[inline]
626618
fn read_i64(&mut self) -> Result<i64, Self::Error> {
627-
read_sleb128!(self, i64)
619+
read_leb128!(self, read_i64_leb128)
628620
}
629621

630622
#[inline]
631623
fn read_i32(&mut self) -> Result<i32, Self::Error> {
632-
read_sleb128!(self, i32)
624+
read_leb128!(self, read_i32_leb128)
633625
}
634626

635627
#[inline]
636628
fn read_i16(&mut self) -> Result<i16, Self::Error> {
637-
read_sleb128!(self, i16)
629+
read_leb128!(self, read_i16_leb128)
638630
}
639631

640632
#[inline]
@@ -646,7 +638,7 @@ impl<'a> serialize::Decoder for Decoder<'a> {
646638

647639
#[inline]
648640
fn read_isize(&mut self) -> Result<isize, Self::Error> {
649-
read_sleb128!(self, isize)
641+
read_leb128!(self, read_isize_leb128)
650642
}
651643

652644
#[inline]
+44-16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![feature(int_bits_const)]
12
#![feature(maybe_uninit_slice)]
23
#![feature(maybe_uninit_uninit_array)]
34

@@ -8,16 +9,28 @@ macro_rules! impl_test_unsigned_leb128 {
89
($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
910
#[test]
1011
fn $test_name() {
12+
// Test 256 evenly spaced values of integer range,
13+
// integer max value, and some "random" numbers.
14+
let mut values = Vec::new();
15+
16+
let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
17+
values.extend((0..256).map(|i| $int_ty::MIN + i * increment));
18+
19+
values.push($int_ty::MAX);
20+
21+
values.extend(
22+
(-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFu64 as $int_ty)),
23+
);
24+
1125
let mut stream = Vec::new();
1226

13-
for x in 0..62 {
27+
for &x in &values {
1428
let mut buf = MaybeUninit::uninit_array();
15-
stream.extend($write_fn_name(&mut buf, (3u64 << x) as $int_ty));
29+
stream.extend($write_fn_name(&mut buf, x));
1630
}
1731

1832
let mut position = 0;
19-
for x in 0..62 {
20-
let expected = (3u64 << x) as $int_ty;
33+
for &expected in &values {
2134
let (actual, bytes_read) = $read_fn_name(&stream[position..]);
2235
assert_eq!(expected, actual);
2336
position += bytes_read;
@@ -34,12 +47,28 @@ impl_test_unsigned_leb128!(test_u128_leb128, write_u128_leb128, read_u128_leb128
3447
impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize);
3548

3649
macro_rules! impl_test_signed_leb128 {
37-
($test_name:ident, $write_fn_name:ident, $int_ty:ident) => {
50+
($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
3851
#[test]
3952
fn $test_name() {
40-
let values: Vec<_> = (-500..500)
41-
.map(|i| ((i as $int_ty).wrapping_mul(0x12345789ABCDEF_i64 as $int_ty)))
42-
.collect();
53+
// Test 256 evenly spaced values of integer range,
54+
// integer max value, and some "random" numbers.
55+
let mut values = Vec::new();
56+
57+
let mut value = $int_ty::MIN;
58+
let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
59+
60+
for _ in 0..256 {
61+
values.push(value);
62+
// The addition in the last loop iteration overflows.
63+
value = value.wrapping_add(increment);
64+
}
65+
66+
values.push($int_ty::MAX);
67+
68+
values.extend(
69+
(-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFi64 as $int_ty)),
70+
);
71+
4372
let mut stream = Vec::new();
4473

4574
for &x in &values {
@@ -48,9 +77,8 @@ macro_rules! impl_test_signed_leb128 {
4877
}
4978

5079
let mut position = 0;
51-
for &x in &values {
52-
let expected = x as i128;
53-
let (actual, bytes_read) = read_signed_leb128(&mut stream, position);
80+
for &expected in &values {
81+
let (actual, bytes_read) = $read_fn_name(&stream[position..]);
5482
assert_eq!(expected, actual);
5583
position += bytes_read;
5684
}
@@ -59,8 +87,8 @@ macro_rules! impl_test_signed_leb128 {
5987
};
6088
}
6189

62-
impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, i16);
63-
impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, i32);
64-
impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, i64);
65-
impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, i128);
66-
impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, isize);
90+
impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, read_i16_leb128, i16);
91+
impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, read_i32_leb128, i32);
92+
impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, read_i64_leb128, i64);
93+
impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, read_i128_leb128, i128);
94+
impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, read_isize_leb128, isize);

0 commit comments

Comments
 (0)