Skip to content

Commit 985b116

Browse files
committed
multiboot2: fix memory issue in boxed_dst_tag
1 parent 3020b6a commit 985b116

File tree

1 file changed

+85
-21
lines changed

1 file changed

+85
-21
lines changed

multiboot2/src/builder/mod.rs

+85-21
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,101 @@ 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::{size_of, size_of_val};
12+
use core::ops::Deref;
13+
use std::mem::align_of_val;
1214

13-
use crate::{TagTrait, TagTypeId};
15+
use crate::{Tag, TagTrait, TagTypeId};
1416

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

0 commit comments

Comments
 (0)