Skip to content

Commit 78de7b3

Browse files
committed
multiboot2-header: more safety for header tag iterator
1 parent 648184d commit 78de7b3

File tree

2 files changed

+65
-21
lines changed

2 files changed

+65
-21
lines changed

multiboot2-header/src/header/mod.rs

+57-20
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ impl<'a> Multiboot2Header<'a> {
9090
pub fn iter(&self) -> Multiboot2HeaderTagIter {
9191
self.inner.tag_iter()
9292
}
93-
9493
/// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
9594
pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
9695
Multiboot2BasicHeader::calc_checksum(magic, arch, length)
@@ -161,6 +160,9 @@ impl Multiboot2BasicHeader {
161160
}
162161

163162
/// Returns a [`Multiboot2HeaderTagIter`].
163+
///
164+
/// # Panics
165+
/// See doc of [`Multiboot2HeaderTagIter`].
164166
pub fn tag_iter(&self) -> Multiboot2HeaderTagIter {
165167
let base_hdr_size = size_of::<Multiboot2BasicHeader>();
166168
if base_hdr_size == self.length as usize {
@@ -196,6 +198,12 @@ impl StructAsBytes for Multiboot2BasicHeader {}
196198

197199
/// Iterator over all tags of a Multiboot2 header. The number of items is derived
198200
/// by the size/length of the header.
201+
///
202+
/// # Panics
203+
/// Panics if the `length`-attribute doesn't match the number of found tags, there are
204+
/// more tags found than technically possible, or if there is more than one end tag.
205+
/// All of these errors come from bigger, underlying problems. Therefore, they are
206+
/// considered as "abort/panic" and not as recoverable errors.
199207
#[derive(Clone)]
200208
pub struct Multiboot2HeaderTagIter {
201209
/// 8-byte aligned base address
@@ -205,6 +213,14 @@ pub struct Multiboot2HeaderTagIter {
205213
n: u32,
206214
/// Size / final value of [`Self::n`].
207215
size: u32,
216+
/// Counts the number of found tags. If more tags are found
217+
/// than technically possible, for example because the length property
218+
/// was invalid and there are hundreds of "End"-tags, we can use
219+
/// this and enforce a hard iteration limit.
220+
tag_count: u32,
221+
/// Marks if the end-tag was found. Together with `tag_count`, this
222+
/// further helps to improve safety when invalid length properties are given.
223+
end_tag_found: bool,
208224
}
209225

210226
impl Multiboot2HeaderTagIter {
@@ -213,33 +229,54 @@ impl Multiboot2HeaderTagIter {
213229
let base = base as *const u8;
214230
let base = unsafe { base.add(base.align_offset(8)) };
215231
let base = base as *const HeaderTag;
216-
Self { base, n: 0, size }
232+
Self {
233+
base,
234+
n: 0,
235+
size,
236+
tag_count: 0,
237+
end_tag_found: false,
238+
}
217239
}
218240
}
219241

220242
impl Iterator for Multiboot2HeaderTagIter {
221243
type Item = *const HeaderTag;
222244

223245
fn next(&mut self) -> Option<Self::Item> {
224-
if self.n < self.size {
225-
// transform to byte ptr => offset works correctly
226-
let ptr = self.base as *const u8;
227-
let ptr = unsafe { ptr.add(self.n as usize) };
228-
let ptr = ptr as *const HeaderTag;
229-
assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned");
230-
let tag = unsafe { &*ptr };
231-
assert!(
232-
tag.size() <= 500,
233-
"no real mb2 header should be bigger than 500bytes - probably wrong memory?! is: {}",
234-
{tag.size()}
235-
);
236-
self.n += tag.size();
237-
// 8-byte alignment of pointer address
238-
self.n += self.n % 8;
239-
Some(ptr)
240-
} else {
241-
None
246+
// no more bytes left to check; length reached
247+
if self.n >= self.size {
248+
return None;
249+
}
250+
251+
// transform to byte ptr => offset works correctly
252+
let ptr = self.base as *const u8;
253+
let ptr = unsafe { ptr.add(self.n as usize) };
254+
let ptr = ptr as *const HeaderTag;
255+
assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned");
256+
let tag = unsafe { &*ptr };
257+
assert!(
258+
tag.size() <= 500,
259+
"no real mb2 header should be bigger than 500bytes - probably wrong memory?! is: {}",
260+
{ tag.size() }
261+
);
262+
assert!(
263+
tag.size() >= 8,
264+
"no real mb2 header tag is smaller than 8 bytes - probably wrong memory?! is: {}",
265+
{ tag.size() }
266+
);
267+
assert!(
268+
!self.end_tag_found,
269+
"There is more than one end tag! Maybe the `length` property is invalid?"
270+
);
271+
self.n += tag.size();
272+
// 8-byte alignment of pointer address
273+
self.n += self.n % 8;
274+
self.tag_count += 1;
275+
if tag.typ() == HeaderTagType::End {
276+
self.end_tag_found = true;
242277
}
278+
assert!(self.tag_count < HeaderTagType::count(), "Invalid Multiboot2 header tags! There are more tags than technically possible! Maybe the `length` property is invalid?");
279+
Some(ptr)
243280
}
244281
}
245282

multiboot2-header/src/tags.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,14 @@ pub enum HeaderTagType {
4747
Relocatable = 10,
4848
}
4949

50-
/// Flags for multiboot2 header tags.
50+
impl HeaderTagType {
51+
/// Returns the number of possible variants.
52+
pub const fn count() -> u32 {
53+
11
54+
}
55+
}
56+
57+
/// Flags for Multiboot2 header tags.
5158
#[repr(u16)]
5259
#[derive(Copy, Clone, Debug, PartialEq)]
5360
pub enum HeaderTagFlag {

0 commit comments

Comments
 (0)