Skip to content

Commit 0e8c391

Browse files
committed
multiboot2: Memory-safe new_boxed
This will replace the existing BoxedDst typ, which has UB.
1 parent 66b7c14 commit 0e8c391

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

multiboot2/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub use smbios::SmbiosTag;
100100
pub use tag::TagHeader;
101101
pub use tag_trait::TagTrait;
102102
pub use tag_type::{TagType, TagTypeId};
103-
pub use util::{parse_slice_as_string, StringError};
103+
pub use util::{new_boxed, parse_slice_as_string, StringError};
104104
pub use vbe_info::{
105105
VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag,
106106
VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes,

multiboot2/src/util.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
//! Various utilities.
22
3-
use crate::ALIGNMENT;
3+
use crate::tag::GenericTag;
4+
use crate::{TagHeader, TagTrait, TagType, ALIGNMENT};
45
use core::fmt;
56
use core::fmt::{Display, Formatter};
67
use core::str::Utf8Error;
8+
use core::{ptr, slice};
9+
#[cfg(feature = "builder")]
10+
use {alloc::alloc::Layout, alloc::boxed::Box};
711

812
/// Error type describing failures when parsing the string from a tag.
913
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -31,6 +35,38 @@ impl core::error::Error for StringError {
3135
}
3236
}
3337

38+
/// Creates a new tag implementing [`TagTrait`] on the heap. This works for
39+
/// sized and unsized tags. However, it only makes sense to use this for tags
40+
/// that are DSTs (unsized), as for the sized ones, you can call a regular
41+
/// constructor and box the result.
42+
///
43+
/// # Parameters
44+
/// - `additional_bytes`: All bytes apart from the default [`TagHeader`] that
45+
/// are included into the tag.
46+
#[cfg(feature = "alloc")]
47+
pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes: &[u8]) -> Box<T> {
48+
let size = size_of::<TagHeader>() + additional_bytes.iter().len();
49+
let alloc_size = increase_to_alignment(size);
50+
let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap();
51+
let heap_ptr = unsafe { alloc::alloc::alloc(layout) };
52+
assert!(!heap_ptr.is_null());
53+
54+
unsafe {
55+
heap_ptr.cast::<u32>().write(T::ID.val());
56+
heap_ptr.cast::<u32>().add(1).write(size as u32);
57+
}
58+
unsafe {
59+
let ptr = heap_ptr.add(size_of::<TagHeader>());
60+
ptr::copy_nonoverlapping(additional_bytes.as_ptr(), ptr, additional_bytes.len());
61+
}
62+
63+
let header = unsafe { heap_ptr.cast::<TagHeader>().as_ref() }.unwrap();
64+
65+
let ptr = ptr_meta::from_raw_parts_mut(heap_ptr.cast(), T::dst_len(header));
66+
67+
unsafe { Box::from_raw(ptr) }
68+
}
69+
3470
/// Parses the provided byte sequence as Multiboot string, which maps to a
3571
/// [`str`].
3672
pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> {
@@ -52,6 +88,8 @@ pub const fn increase_to_alignment(size: usize) -> usize {
5288
#[cfg(test)]
5389
mod tests {
5490
use super::*;
91+
use crate::tag::GenericTag;
92+
use crate::CommandLineTag;
5593

5694
#[test]
5795
fn test_parse_slice_as_string() {
@@ -87,4 +125,13 @@ mod tests {
87125
assert_eq!(increase_to_alignment(8), 8);
88126
assert_eq!(increase_to_alignment(9), 16);
89127
}
128+
129+
#[test]
130+
fn test_new_boxed() {
131+
let tag = new_boxed::<GenericTag>(&[0, 1, 2, 3]);
132+
assert_eq!(tag.header().typ, GenericTag::ID);
133+
{}
134+
let tag = new_boxed::<CommandLineTag>("hello\0".as_bytes());
135+
assert_eq!(tag.cmdline(), Ok("hello"));
136+
}
90137
}

0 commit comments

Comments
 (0)