Skip to content

Commit a72a674

Browse files
authored
Merge pull request #152 from rust-osdev/dev4
multiboot2: fix memory issue in boxed_dst_tag
2 parents 3020b6a + bf137e8 commit a72a674

File tree

1 file changed

+84
-21
lines changed

1 file changed

+84
-21
lines changed

multiboot2/src/builder/mod.rs

+84-21
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,100 @@ pub use information::InformationBuilder;
88
use alloc::alloc::alloc;
99
use alloc::boxed::Box;
1010
use core::alloc::Layout;
11-
use core::mem::size_of;
11+
use core::mem::{align_of_val, size_of, size_of_val};
12+
use core::ops::Deref;
1213

13-
use crate::{TagTrait, TagTypeId};
14+
use crate::{Tag, TagTrait, TagTypeId};
1415

1516
/// Create a boxed tag with the given content.
17+
///
18+
/// # Parameters
19+
/// - `typ` - The given [`TagTypeId`]
20+
/// - `content` - All payload bytes of the DST tag without the tag type or the
21+
/// size. The memory is only read and can be discarded afterwards.
1622
pub(super) fn boxed_dst_tag<T: TagTrait<Metadata = usize> + ?Sized>(
1723
typ: impl Into<TagTypeId>,
1824
content: &[u8],
1925
) -> Box<T> {
20-
// based on https://stackoverflow.com/a/64121094/2192464
21-
let (layout, size_offset) = Layout::new::<TagTypeId>()
22-
.extend(Layout::new::<u32>())
23-
.unwrap();
24-
let (layout, inner_offset) = layout
25-
.extend(Layout::array::<usize>(content.len()).unwrap())
26-
.unwrap();
26+
// Currently, I do not find a nice way of making this dynamic so that also
27+
// miri is happy. But it seems that 4 is fine.
28+
const ALIGN: usize = 4;
29+
30+
let tag_size = size_of::<TagTypeId>() + size_of::<u32>() + content.len();
31+
// round up to the next multiple of 8
32+
// Rust uses this convention for all types. I found out so by checking
33+
// miris output of the corresponding unit test.
34+
let alloc_size = (tag_size + 7) & !0b111;
35+
let layout = Layout::from_size_align(alloc_size, ALIGN).unwrap();
2736
let ptr = unsafe { alloc(layout) };
2837
assert!(!ptr.is_null());
38+
39+
// write tag content to memory
2940
unsafe {
30-
// initialize the content as good as we can
31-
ptr.cast::<TagTypeId>().write(typ.into());
32-
ptr.add(size_offset).cast::<u32>().write(
33-
(content.len() + size_of::<TagTypeId>() + size_of::<u32>())
34-
.try_into()
35-
.unwrap(),
36-
);
37-
// initialize body
38-
let content_ptr = ptr.add(inner_offset);
39-
for (idx, val) in content.iter().enumerate() {
40-
content_ptr.add(idx).write(*val);
41+
// write tag type
42+
let ptrx = ptr.cast::<TagTypeId>();
43+
ptrx.write(typ.into());
44+
45+
// write tag size
46+
let ptrx = ptrx.add(1).cast::<u32>();
47+
ptrx.write(tag_size as u32);
48+
49+
// write rest of content
50+
let ptrx = ptrx.add(1).cast::<u8>();
51+
let tag_content_slice = core::slice::from_raw_parts_mut(ptrx, content.len());
52+
for (i, &byte) in content.iter().enumerate() {
53+
tag_content_slice[i] = byte;
54+
}
55+
}
56+
57+
let base_tag = unsafe { &*ptr.cast::<Tag>() };
58+
let raw: *mut T = ptr_meta::from_raw_parts_mut(ptr.cast(), T::dst_size(base_tag));
59+
60+
unsafe {
61+
let boxed = Box::from_raw(raw);
62+
let reference: &T = boxed.deref();
63+
// If this panics, please create an issue on GitHub.
64+
assert_eq!(size_of_val(reference), alloc_size);
65+
assert_eq!(align_of_val(reference), ALIGN);
66+
boxed
67+
}
68+
}
69+
70+
#[cfg(test)]
71+
mod tests {
72+
use super::*;
73+
74+
const METADATA_SIZE: usize = 8;
75+
76+
#[derive(ptr_meta::Pointee)]
77+
#[repr(C)]
78+
struct CustomTag {
79+
typ: TagTypeId,
80+
size: u32,
81+
string: [u8],
82+
}
83+
84+
impl CustomTag {
85+
fn string(&self) -> Result<&str, core::str::Utf8Error> {
86+
Tag::get_dst_str_slice(&self.string)
4187
}
42-
Box::from_raw(ptr_meta::from_raw_parts_mut(ptr as *mut (), content.len()))
88+
}
89+
90+
impl TagTrait for CustomTag {
91+
fn dst_size(base_tag: &Tag) -> usize {
92+
assert!(base_tag.size as usize >= METADATA_SIZE);
93+
base_tag.size as usize - METADATA_SIZE
94+
}
95+
}
96+
97+
#[test]
98+
fn test_boxed_dst_tag() {
99+
let tag_type_id = 1337_u32;
100+
let content = "hallo";
101+
102+
let tag = boxed_dst_tag::<CustomTag>(tag_type_id, content.as_bytes());
103+
assert_eq!(tag.typ, tag_type_id);
104+
assert_eq!(tag.size as usize, METADATA_SIZE + content.len());
105+
assert_eq!(tag.string(), Ok(content));
43106
}
44107
}

0 commit comments

Comments
 (0)