Skip to content

Commit 678cef7

Browse files
committed
Deny invalid bits which are out of range
1 parent 4b4a504 commit 678cef7

File tree

6 files changed

+55
-2
lines changed

6 files changed

+55
-2
lines changed

bitbybit/src/bitfield/parsing.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,9 @@ fn parse_field(base_data_size: usize, field: &Field) -> Result<FieldDefinition>
380380
return Err(Error::new_spanned(
381381
field.attrs.first(),
382382
format!(
383-
"bitfield!: Array-field {} requires {number_of_bits_indexed} bits for the array, but only has ({})", field_name, base_data_size
383+
"bitfield!: Array-field {} requires {number_of_bits_indexed} bits for the array, but only has ({})",
384+
field_name,
385+
base_data_size
384386
)
385387
));
386388
}
@@ -396,6 +398,8 @@ fn parse_field(base_data_size: usize, field: &Field) -> Result<FieldDefinition>
396398
}
397399
}
398400

401+
verify_bits_in_range(field, field_name, &ranges, base_data_size)?;
402+
399403
let (custom_type, getter_type, setter_type) = if field_type_size_from_data_type.is_none() {
400404
parse_enumeration(ty, &primitive_type)?
401405
} else {
@@ -430,11 +434,30 @@ fn parse_field(base_data_size: usize, field: &Field) -> Result<FieldDefinition>
430434
doc_comment,
431435
array: indexed_count.map(|count| (count, indexed_stride.unwrap())),
432436
field_type_size_from_data_type: field_type_size_from_data_type.map(|v| v.0),
433-
is_signed: field_type_size_from_data_type.map_or(false, |v| v.1),
437+
is_signed: field_type_size_from_data_type.is_some_and(|v| v.1),
434438
unsigned_field_type,
435439
})
436440
}
437441

442+
fn verify_bits_in_range(
443+
field: &Field,
444+
field_name: &Ident,
445+
ranges: &[Range<usize>],
446+
base_data_size: usize,
447+
) -> syn::Result<()> {
448+
let highest_bit_index_in_ranges = ranges.iter().map(|range| range.end).max().unwrap_or(0);
449+
if highest_bit_index_in_ranges > base_data_size {
450+
return Err(Error::new_spanned(
451+
field.attrs.first(),
452+
format!(
453+
"bitfield!: Field {} requires {} bits, but only has ({})",
454+
field_name, highest_bit_index_in_ranges, base_data_size
455+
),
456+
));
457+
}
458+
Ok(())
459+
}
460+
438461
/// Parses the arguments of a field. At the beginning and after each comma, Reset is used. After
439462
/// that, the various take_xxx functions are used to switch states.
440463
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use bitbybit::bitfield;
2+
3+
#[bitfield(u32, default = 0)]
4+
struct Test {
5+
#[bit(32, rw)]
6+
field: bool,
7+
}
8+
9+
fn main() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: bitfield!: Field field requires 33 bits, but only has (32)
2+
--> tests/no_compile/invalid_bit_pos.rs:5:5
3+
|
4+
5 | #[bit(32, rw)]
5+
| ^^^^^^^^^^^^^^
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use bitbybit::bitfield;
2+
3+
#[bitfield(u32, default = 0)]
4+
struct Test {
5+
#[bits(31..=32, rw)]
6+
field: u2,
7+
}
8+
9+
fn main() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: bitfield!: Field field requires 33 bits, but only has (32)
2+
--> tests/no_compile/invalid_bits_pos.rs:5:5
3+
|
4+
5 | #[bits(31..=32, rw)]
5+
| ^^^^^^^^^^^^^^^^^^^^

bitbybit/tests/tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ fn all_tests() {
1010
t.compile_fail("tests/no_compile/non_exhaustive_bitenum.rs");
1111
t.compile_fail("tests/no_compile/invalid_field_range.rs");
1212
t.compile_fail("tests/no_compile/invalid_field_access.rs");
13+
t.compile_fail("tests/no_compile/invalid_bit_pos.rs");
14+
t.compile_fail("tests/no_compile/invalid_bits_pos.rs");
1315
}

0 commit comments

Comments
 (0)