Skip to content

Commit c69161c

Browse files
authored
Fix reading little endian fields in LSB order when not aligned to byte boundaries (#604)
Co-authored-by: zebrapurring <>
1 parent 0902ae0 commit c69161c

File tree

6 files changed

+289
-6
lines changed

6 files changed

+289
-6
lines changed

src/impls/primitive.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,13 @@ macro_rules! ImplDekuReadBits {
191191
}
192192
}
193193

194-
// if read from Lsb order and it's escpecially cursed since its not just within one byte...
194+
// if read from Lsb order and it's especially cursed since its not just within one byte...
195195
// read_bits returned: [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1]
196196
// | second | first |
197197
// we want to read from right to left when lsb (without using BitVec BitFields)
198198
//
199199
// Turning this into [0x23, 0x01] (then appending till type size)
200-
if order == Order::Lsb0 && bit_slice.len() > 8 {
200+
if order == Order::Lsb0 && bit_slice.len() > 8 && pad != 0 {
201201
let mut bits = BitVec::<u8, Msb0>::with_capacity(bit_slice.len() + pad);
202202

203203
bits.extend_from_bitslice(&bit_slice);

src/reader.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,18 @@ impl<R: Read + Seek> Reader<R> {
333333
#[cfg(feature = "logging")]
334334
log::trace!("extend(used): {}", used);
335335
ret.extend_from_bitslice(used);
336-
if let Some(front_bits) = front_bits {
336+
if let Some(mut front_bits) = front_bits {
337+
let front_bits_le = front_bits
338+
.chunks(8)
339+
.rev()
340+
.flat_map(|chunk| chunk.iter().by_vals())
341+
.collect::<BitVec<u8, _>>();
342+
if amt % 8 != 0 {
343+
// WA: required to apply endianness for cases where the field is not aligned with the byte boundary
344+
// (see https://github.com/sharksforarms/deku/issues/603)
345+
front_bits = &front_bits_le;
346+
}
347+
337348
#[cfg(feature = "logging")]
338349
log::trace!("extend(front_bits): {}", front_bits);
339350
ret.extend_from_bitslice(front_bits);

tests/bit_order.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ mod tests {
420420

421421
let string_data = data
422422
.iter()
423-
.map(|f| (format!("{f:08b}").chars().rev().collect()))
423+
.map(|f| format!("{f:08b}").chars().rev().collect())
424424
.collect::<Vec<String>>()
425425
.join("");
426426

tests/test_compile/cases/enum_validation.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,16 @@ error: DekuWrite: `id_type` with non-unit variants requires primitive representa
154154
213 | Base = 0x00,
155155
| ^^^^
156156

157-
error[E0732]: `#[repr(inttype)]` must be specified
157+
error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants
158158
--> tests/test_compile/cases/enum_validation.rs:138:1
159159
|
160160
138 | pub enum Test19 {
161161
| ^^^^^^^^^^^^^^^
162+
139 | A = 0,
163+
| - explicit discriminant specified here
164+
140 | #[deku(id_pat = "_")]
165+
141 | B(u8),
166+
| - non-unit discriminant declared here
162167

163168
error[E0308]: `?` operator has incompatible types
164169
--> tests/test_compile/cases/enum_validation.rs:104:28

tests/test_compile/cases/no_deku_id_generic_enum.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error[E0599]: no method named `deku_id` found for enum `Body` in the current scope
22
--> tests/test_compile/cases/no_deku_id_generic_enum.rs:12:7
33
|
4-
5 | pub enum Body<T: for<'a> DekuReader<'a>> {
4+
5 | pub enum Body<T: for<'a> DekuReader<'a>> {
55
| ---------------------------------------- method `deku_id` not found for this enum
66
...
77
12 | n.deku_id();

tests/test_lsb_le.rs

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#![cfg(feature = "bits")]
2+
3+
use assert_hex::assert_eq_hex;
4+
use deku::prelude::*;
5+
6+
#[test]
7+
fn test_lsb_le_misaligned_middle() {
8+
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
9+
#[deku(bit_order = "lsb", endian = "little")]
10+
pub struct TestStruct {
11+
#[deku(bits = 1)]
12+
pub field_a: u8, // Bit 0
13+
#[deku(bits = 1)]
14+
pub field_b: u8, // Bit 1
15+
#[deku(bits = 1)]
16+
pub field_c: u8, // Bit 2
17+
#[deku(bits = 27)]
18+
pub field_d: u32, // Bits 3-29
19+
#[deku(bits = 2)]
20+
pub field_e: u8, // Bits 30-31
21+
}
22+
23+
// Interpreted as Little Endian:
24+
// 31 23 15 7 0
25+
// |-0xF1--| |-0x32--| |-0xDC--| |-0xAD--|
26+
// 1111 0001 0011 0010 1101 1100 1010 1101
27+
// 11 0001 1101 1100 0011 0010 1010 1 - Value decoded by deku@0902ae0
28+
// ^-----^ ^-----------------^ ^----^
29+
// used front_bits leftover
30+
// EEDD DDDD DDDD DDDD DDDD DDDD DDDD DCBA
31+
const RAW_DATA: &[u8] = &[0xAD, 0xDC, 0x32, 0xF1];
32+
33+
let parsed = TestStruct::from_bytes((RAW_DATA, 0)).unwrap().1;
34+
35+
let expected = TestStruct {
36+
field_a: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0000_0001) as u8),
37+
field_b: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0000_0002) >> 1) as u8,
38+
field_c: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0000_0004) >> 2) as u8,
39+
field_d: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x3FFF_FFF8) >> 3),
40+
field_e: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0xC000_0000) >> 30) as u8,
41+
};
42+
43+
assert_eq_hex!(
44+
expected,
45+
TestStruct {
46+
field_a: 0x1,
47+
field_b: 0x0,
48+
field_c: 0x1,
49+
field_d: 0x6265B95,
50+
field_e: 0x3,
51+
},
52+
"Incorrect manual calculation"
53+
);
54+
assert_eq_hex!(parsed, expected, "Invalid deku calculation");
55+
}
56+
57+
#[test]
58+
fn test_lsb_le_misaligned_right() {
59+
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
60+
#[deku(bit_order = "lsb", endian = "little")]
61+
pub struct TestStruct {
62+
#[deku(bits = 5)]
63+
pub field_a: u8, // Bits 0-4
64+
#[deku(bits = 27)]
65+
pub field_b: u32, // Bits 5-31
66+
}
67+
68+
const RAW_DATA: &[u8] = &[0xAD, 0xDC, 0x32, 0xF1];
69+
70+
let parsed = TestStruct::from_bytes((RAW_DATA, 0)).unwrap().1;
71+
72+
let expected = TestStruct {
73+
field_a: (u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0000_001F) as u8,
74+
field_b: (u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0xFFFF_FFE0) >> 5,
75+
};
76+
77+
assert_eq_hex!(
78+
expected,
79+
TestStruct {
80+
field_a: 0xD,
81+
field_b: 0x0789_96E5,
82+
},
83+
"Incorrect manual calculation"
84+
);
85+
assert_eq_hex!(parsed, expected, "Invalid deku calculation");
86+
}
87+
88+
#[test]
89+
fn test_lsb_le_misaligned_left() {
90+
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
91+
#[deku(bit_order = "lsb", endian = "little")]
92+
pub struct TestStruct {
93+
#[deku(bits = 27)]
94+
pub field_a: u32, // Bits 0-26
95+
#[deku(bits = 5)]
96+
pub field_b: u8, // Bits 27-31
97+
}
98+
99+
const RAW_DATA: &[u8] = &[0xAD, 0xDC, 0x32, 0xF1];
100+
101+
let parsed = TestStruct::from_bytes((RAW_DATA, 0)).unwrap().1;
102+
103+
let expected = TestStruct {
104+
field_a: (u32::from_le_bytes(RAW_DATA.try_into().unwrap())) & 0x07FF_FFFF,
105+
field_b: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0xF800_0000) >> 27) as u8,
106+
};
107+
108+
assert_eq_hex!(
109+
expected,
110+
TestStruct {
111+
field_a: 0x0132_DCAD,
112+
field_b: 0x1E,
113+
},
114+
"Incorrect manual calculation"
115+
);
116+
assert_eq_hex!(parsed, expected, "Invalid deku calculation");
117+
}
118+
119+
#[test]
120+
fn test_lsb_le_aligned_right() {
121+
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
122+
#[deku(bit_order = "lsb", endian = "little")]
123+
pub struct TestStruct {
124+
#[deku(bits = 8)]
125+
pub field_a: u8, // Bits 0-7
126+
#[deku(bits = 24)]
127+
pub field_b: u32, // Bits 8-31
128+
}
129+
130+
const RAW_DATA: &[u8] = &[0xAD, 0xDC, 0x32, 0xF1];
131+
132+
let parsed = TestStruct::from_bytes((RAW_DATA, 0)).unwrap().1;
133+
134+
let expected = TestStruct {
135+
field_a: (u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0000_00FF) as u8,
136+
field_b: (u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0xFFFF_FF00) >> 8,
137+
};
138+
139+
assert_eq_hex!(
140+
expected,
141+
TestStruct {
142+
field_a: 0xAD,
143+
field_b: 0x00F1_32DC,
144+
},
145+
"Incorrect manual calculation"
146+
);
147+
assert_eq_hex!(parsed, expected, "Invalid deku calculation");
148+
}
149+
150+
#[test]
151+
fn test_lsb_le_aligned_left() {
152+
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
153+
#[deku(bit_order = "lsb", endian = "little")]
154+
pub struct TestStruct {
155+
#[deku(bits = 24)]
156+
pub field_a: u32, // Bits 0-23
157+
#[deku(bits = 8)]
158+
pub field_b: u8, // Bits 24-31
159+
}
160+
161+
const RAW_DATA: &[u8] = &[0xAD, 0xDC, 0x32, 0xF1];
162+
163+
let parsed = TestStruct::from_bytes((RAW_DATA, 0)).unwrap().1;
164+
165+
let expected = TestStruct {
166+
field_a: (u32::from_le_bytes(RAW_DATA.try_into().unwrap())) & 0x00FF_FFFF,
167+
field_b: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0xFF00_0000) >> 24) as u8,
168+
};
169+
170+
assert_eq_hex!(
171+
expected,
172+
TestStruct {
173+
field_a: 0x0032_DCAD,
174+
field_b: 0xF1,
175+
},
176+
"Incorrect manual calculation"
177+
);
178+
assert_eq_hex!(parsed, expected, "Invalid deku calculation");
179+
}
180+
181+
#[test]
182+
fn test_lsb_le_aligned_mixed() {
183+
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
184+
#[deku(bit_order = "lsb", endian = "little")]
185+
pub struct TestStruct {
186+
#[deku(bits = 16)]
187+
pub field_a: u16, // Bits 0-15
188+
#[deku(bits = 1)]
189+
pub field_b: u8, // Bit 16
190+
#[deku(bits = 2)]
191+
pub field_c: u8, // Bits 17-18
192+
#[deku(bits = 4)]
193+
pub field_d: u8, // Bits 19-22
194+
#[deku(bits = 6)]
195+
pub field_e: u8, // Bits 23-28
196+
#[deku(bits = 3)]
197+
pub field_f: u8, // Bits 29-31
198+
}
199+
200+
const RAW_DATA: &[u8] = &[0xAD, 0xDC, 0x32, 0xF1];
201+
202+
let parsed = TestStruct::from_bytes((RAW_DATA, 0)).unwrap().1;
203+
204+
let expected = TestStruct {
205+
field_a: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0000_FFFF) as u16),
206+
field_b: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0001_0000) >> 16) as u8,
207+
field_c: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0006_0000) >> 17) as u8,
208+
field_d: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x0078_0000) >> 19) as u8,
209+
field_e: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0x1F80_0000) >> 23) as u8,
210+
field_f: ((u32::from_le_bytes(RAW_DATA.try_into().unwrap()) & 0xE000_0000) >> 29) as u8,
211+
};
212+
213+
assert_eq_hex!(
214+
expected,
215+
TestStruct {
216+
field_a: 0xDCAD,
217+
field_b: 0x0,
218+
field_c: 0x1,
219+
field_d: 0x6,
220+
field_e: 0x22,
221+
field_f: 0x7,
222+
},
223+
"Incorrect manual calculation"
224+
);
225+
assert_eq_hex!(parsed, expected, "Invalid deku calculation");
226+
}
227+
228+
#[test]
229+
fn test_lsb_le_misaligned_2() {
230+
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
231+
#[deku(endian = "little", bit_order = "lsb")]
232+
struct Header {
233+
#[deku(bits = 20)]
234+
foo: u32,
235+
#[deku(bits = 12)]
236+
bar: u32,
237+
}
238+
let reference = Header {
239+
foo: 0xabc,
240+
bar: 0xdef,
241+
};
242+
let data = reference.to_bytes().unwrap();
243+
assert_eq!(data, b"\xbc\x0a\xf0\xde");
244+
let (_, actual) = Header::from_bytes((&data[..], 0)).unwrap();
245+
assert_eq!(actual, reference);
246+
}
247+
248+
#[test]
249+
fn test_invalid_lsb_bit_split_squashfs_v3() {
250+
#[derive(Debug, DekuRead, DekuWrite, PartialEq)]
251+
#[deku(endian = "little", bit_order = "lsb")]
252+
pub struct Dir {
253+
#[deku(bits = "19")]
254+
pub file_size: u32,
255+
#[deku(bits = "13")]
256+
pub offset: u16,
257+
}
258+
259+
let expected_file_size = 38u32;
260+
let expected_offset = 44u16;
261+
let combined = ((expected_offset as u32) << 19) | expected_file_size;
262+
let test_data = combined.to_le_bytes();
263+
let (_, result) = Dir::from_bytes((&test_data, 0)).unwrap();
264+
assert_eq!(result.file_size, expected_file_size);
265+
assert_eq!(result.offset, expected_offset);
266+
assert_eq!(test_data, &*result.to_bytes().unwrap());
267+
}

0 commit comments

Comments
 (0)