Skip to content

Commit 4b7eeaf

Browse files
cemonemwucke13
authored andcommitted
fix: leb128 parsing of integers
Signed-off-by: Cem Onem <cem.oenem@dlr.de>
1 parent 43a19fc commit 4b7eeaf

File tree

1 file changed

+94
-31
lines changed

1 file changed

+94
-31
lines changed

src/core/reader/types/values.rs

Lines changed: 94 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@ impl WasmReader<'_> {
4343

4444
/// Parses a variable-length `u64` (can be casted to a smaller uint if the result fits)
4545
/// Taken from <https://github.com/bytecodealliance/wasm-tools>
46+
#[allow(unused)]
4647
pub fn read_var_u64(&mut self) -> Result<u64> {
4748
let mut result = 0;
4849
let mut shift = 0;
4950

5051
loop {
5152
let mut byte = self.read_u8()?;
53+
// maximum allowed num of leb bytes for u32 is ceil(32.0/7.0) == 10
54+
// shift >= 63 checks we're at the 10th bit or larger
55+
// byte != 1 checks whether (this byte lost bits when shifted) or (the continuation bit is set)
5256
if shift == 63 && byte != 0 && byte != 1 {
5357
while byte & Self::CONTINUATION_BIT != 0 {
5458
byte = self.read_u8()?;
@@ -70,9 +74,25 @@ impl WasmReader<'_> {
7074
/// Parses a variable-length `u32` as specified by [LEB128](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128).
7175
/// Note: If `Err`, the [WasmReader] object is no longer guaranteed to be in a valid state
7276
pub fn read_var_u32(&mut self) -> Result<u32> {
73-
Self::read_var_u64(self)?
74-
.try_into()
75-
.map_err(|_| Error::Overflow)
77+
let mut result: u32 = 0;
78+
let mut shift = 0;
79+
80+
loop {
81+
let byte = self.read_u8()?;
82+
result |= ((byte & 0x7F) as u32) << shift;
83+
// maximum allowed num of leb bytes for u32 is ceil(32.0/7.0) == 5
84+
// shift >= 28 checks we're at the 5th bit or larger
85+
// byte >> 32-28 checks whether (this byte lost bits when shifted) or (the continuation bit is set)
86+
if shift >= 28 && byte >> (32 - shift) != 0 {
87+
return Err(Error::Overflow);
88+
}
89+
90+
if byte & Self::CONTINUATION_BIT == 0 {
91+
return Ok(result);
92+
}
93+
94+
shift += 7;
95+
}
7696
}
7797

7898
pub fn read_var_f64(&mut self) -> Result<u64> {
@@ -81,46 +101,74 @@ impl WasmReader<'_> {
81101
Ok(word)
82102
}
83103

104+
/// Adapted from <https://github.com/bytecodealliance/wasm-tools>
84105
pub fn read_var_i32(&mut self) -> Result<i32> {
85106
let mut result: i32 = 0;
86107
let mut shift: u32 = 0;
87108

88-
let mut byte: i32;
89109
loop {
90-
byte = self.read_u8()? as i32;
91-
result |= (byte & 0b01111111) << shift;
110+
let byte = self.read_u8()?;
111+
result |= ((byte & 0x7F) as i32) << shift;
112+
113+
if shift >= 28 {
114+
// maximum allowed num of leb bytes for u32 is ceil(32.0/7.0) == 5
115+
// shift >= 28 checks we're at the 5th bit or larger
116+
let there_are_more_bytes = (byte & Self::CONTINUATION_BIT) != 0;
117+
let ashifted_unused_bits = (byte << 1) as i8 >> (32 - shift);
118+
// the top unused bits of 35 bytes should be either 111 for negative numbers or 000 for positive numbers
119+
// therefore ashifted_unused_bits should be -1 or 0
120+
if there_are_more_bytes || (ashifted_unused_bits != 0 && ashifted_unused_bits != -1)
121+
{
122+
return Err(Error::Overflow);
123+
} else {
124+
// no need to ashift unfilled bits, all 32 bits are filled
125+
return Ok(result);
126+
}
127+
}
128+
92129
shift += 7;
130+
93131
if (byte & 0b10000000) == 0 {
94132
break;
95133
}
96134
}
97135

98-
if (shift < mem::size_of::<i32>() as u32 * 8) && (byte & 0x40 != 0) {
99-
result |= !0 << shift;
100-
}
101-
102-
Ok(result)
136+
// fill in unfilled bits with sign bit
137+
let ashift = mem::size_of::<i32>() * 8 - shift as usize;
138+
Ok((result << ashift) >> ashift)
103139
}
104140

105141
pub fn read_var_i33(&mut self) -> Result<i64> {
106142
let mut result: i64 = 0;
107-
let mut shift: u64 = 0;
143+
let mut shift: u32 = 0;
108144

109-
let mut byte: i64;
110145
loop {
111-
byte = self.read_u8()? as i64;
112-
result |= (byte & 0b0111_1111) << shift;
146+
let byte = self.read_u8()?;
147+
result |= ((byte & 0x7F) as i64) << shift;
148+
149+
if shift >= 28 {
150+
// maximum allowed num of leb bytes for i33 is ceil(33.0/7.0) == 5
151+
// shift >= 28 checks we're at the 5th bit or larger
152+
let there_are_more_bytes = (byte & Self::CONTINUATION_BIT) != 0;
153+
let ashifted_unused_bits = (byte << 1) as i8 >> (33 - shift);
154+
// the top unused bits of 35 bytes should be either 11 for negative numbers or 00 for positive numbers
155+
// therefore ashifted_unused_bits should be -1 or 0
156+
if there_are_more_bytes || (ashifted_unused_bits != 0 && ashifted_unused_bits != -1)
157+
{
158+
return Err(Error::Overflow);
159+
}
160+
}
161+
113162
shift += 7;
114-
if (byte & 0b1000_0000) == 0 {
163+
164+
if (byte & 0b10000000) == 0 {
115165
break;
116166
}
117167
}
118168

119-
if shift < 33 && (byte & 0x40 != 0) {
120-
result |= !0 << shift;
121-
}
122-
123-
Ok(result)
169+
// fill in unfilled bits with sign bit
170+
let ashift = mem::size_of::<i64>() * 8 - shift as usize;
171+
Ok((result << ashift) >> ashift)
124172
}
125173

126174
pub fn read_var_f32(&mut self) -> Result<u32> {
@@ -141,23 +189,38 @@ impl WasmReader<'_> {
141189

142190
pub fn read_var_i64(&mut self) -> Result<i64> {
143191
let mut result: i64 = 0;
144-
let mut shift: u64 = 0;
192+
let mut shift: u32 = 0;
145193

146-
let mut byte: i64;
147194
loop {
148-
byte = self.read_u8()? as i64;
149-
result |= (byte & 0b0111_1111) << shift;
195+
let byte = self.read_u8()?;
196+
result |= ((byte & 0x7F) as i64) << shift;
197+
198+
if shift >= 63 {
199+
// maximum allowed num of leb bytes for i33 is ceil(64.0/7.0) == 10
200+
// shift >= 63 checks we're at the 10th bit or larger
201+
let there_are_more_bytes = (byte & Self::CONTINUATION_BIT) != 0;
202+
let ashifted_unused_bits = (byte << 1) as i8 >> (64 - shift);
203+
// the top unused bits of 70 bytes should be either 111111 for negative numbers or 000000 for positive numbers
204+
// therefore ashifted_unused_bits should be -1 or 0
205+
if there_are_more_bytes || (ashifted_unused_bits != 0 && ashifted_unused_bits != -1)
206+
{
207+
return Err(Error::Overflow);
208+
} else {
209+
// no need to ashift unfilled bits, all 64 bits are filled
210+
return Ok(result);
211+
}
212+
}
213+
150214
shift += 7;
151-
if (byte & 0b1000_0000) == 0 {
215+
216+
if (byte & 0b10000000) == 0 {
152217
break;
153218
}
154219
}
155220

156-
if shift < 64 && (byte & 0x40 != 0) {
157-
result |= !0 << shift;
158-
}
159-
160-
Ok(result)
221+
// fill in unfilled bits with sign bit
222+
let ashift = mem::size_of::<i64>() * 8 - shift as usize;
223+
Ok((result << ashift) >> ashift)
161224
}
162225

163226
/// Note: If `Err`, the [WasmReader] object is no longer guaranteed to be in a valid state

0 commit comments

Comments
 (0)