Skip to content

Commit 43bc66d

Browse files
committed
multiboot2: prevent panic in framebuffer_tag()
1 parent faf6b6a commit 43bc66d

File tree

3 files changed

+59
-18
lines changed

3 files changed

+59
-18
lines changed

multiboot2/Changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## Unreleased
44
- MSRV is 1.56.1
5+
- **BREAKING** fixed lifetime issues: `VBEInfoTag` is no longer static
6+
- fixed another internal lifetime issue
7+
- `BootInformation::framebuffer_tag()` now returns
8+
`Option<Result<FramebufferTag, UnknownFramebufferType>>` instead of
9+
`Option<FramebufferTag>` which prevents a possible panic. If the `--unstable`
10+
feature is used, `UnknownFramebufferType` implements `core::error::Error`.
511

612
## 0.14.2 (2023-03-17)
713
- documentation fixes

multiboot2/src/framebuffer.rs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::tag_type::Tag;
22
use crate::Reader;
33
use core::slice;
4+
use derive_more::Display;
45

56
/// The VBE Framebuffer information Tag.
67
#[derive(Debug, PartialEq, Eq)]
@@ -28,6 +29,17 @@ pub struct FramebufferTag<'a> {
2829
pub buffer_type: FramebufferType<'a>,
2930
}
3031

32+
/// Helper struct for [`FramebufferType`].
33+
#[derive(Debug, PartialEq, Eq)]
34+
#[repr(u8)]
35+
pub enum FramebufferTypeId {
36+
Indexed = 0,
37+
#[allow(clippy::upper_case_acronyms)]
38+
RGB = 1,
39+
Text = 2,
40+
// spec says: there may be more variants in the future
41+
}
42+
3143
/// The type of framebuffer.
3244
#[derive(Debug, PartialEq, Eq)]
3345
pub enum FramebufferType<'a> {
@@ -79,7 +91,16 @@ pub struct FramebufferColor {
7991
pub blue: u8,
8092
}
8193

82-
pub fn framebuffer_tag(tag: &Tag) -> FramebufferTag {
94+
/// Error when an unknown [`FramebufferTypeId`] is found.
95+
#[derive(Debug, Copy, Clone, Display, PartialEq, Eq)]
96+
#[display(fmt = "Unknown framebuffer type _0")]
97+
pub struct UnknownFramebufferType(u8);
98+
99+
#[cfg(feature = "unstable")]
100+
impl core::error::Error for UnknownFramebufferType {}
101+
102+
/// Transforms a [`Tag`] into a [`FramebufferTag`].
103+
pub fn framebuffer_tag(tag: &Tag) -> Result<FramebufferTag, UnknownFramebufferType> {
83104
let mut reader = Reader::new(tag as *const Tag);
84105
reader.skip(8);
85106
let address = reader.read_u64();
@@ -88,20 +109,27 @@ pub fn framebuffer_tag(tag: &Tag) -> FramebufferTag {
88109
let height = reader.read_u32();
89110
let bpp = reader.read_u8();
90111
let type_no = reader.read_u8();
91-
reader.skip(2); // In the multiboot spec, it has this listed as a u8 _NOT_ a u16.
92-
// Reading the GRUB2 source code reveals it is in fact a u16.
93-
let buffer_type = match type_no {
94-
0 => {
112+
// In the multiboot spec, it has this listed as a u8 _NOT_ a u16.
113+
// Reading the GRUB2 source code reveals it is in fact a u16.
114+
reader.skip(2);
115+
let buffer_type_id = match type_no {
116+
0 => Ok(FramebufferTypeId::Indexed),
117+
1 => Ok(FramebufferTypeId::RGB),
118+
2 => Ok(FramebufferTypeId::Text),
119+
id => Err(UnknownFramebufferType(id)),
120+
}?;
121+
let buffer_type = match buffer_type_id {
122+
FramebufferTypeId::Indexed => {
95123
let num_colors = reader.read_u32();
96124
let palette = unsafe {
97125
slice::from_raw_parts(
98126
reader.current_address() as *const FramebufferColor,
99127
num_colors as usize,
100128
)
101-
} as &'static [FramebufferColor];
129+
} as &[FramebufferColor];
102130
FramebufferType::Indexed { palette }
103131
}
104-
1 => {
132+
FramebufferTypeId::RGB => {
105133
let red_pos = reader.read_u8(); // These refer to the bit positions of the LSB of each field
106134
let red_mask = reader.read_u8(); // And then the length of the field from LSB to MSB
107135
let green_pos = reader.read_u8();
@@ -123,16 +151,15 @@ pub fn framebuffer_tag(tag: &Tag) -> FramebufferTag {
123151
},
124152
}
125153
}
126-
2 => FramebufferType::Text,
127-
_ => panic!("Unknown framebuffer type: {}", type_no),
154+
FramebufferTypeId::Text => FramebufferType::Text,
128155
};
129156

130-
FramebufferTag {
157+
Ok(FramebufferTag {
131158
address,
132159
pitch,
133160
width,
134161
height,
135162
bpp,
136163
buffer_type,
137-
}
164+
})
138165
}

multiboot2/src/lib.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ extern crate std;
4040
use core::fmt;
4141
use derive_more::Display;
4242

43+
use crate::framebuffer::UnknownFramebufferType;
4344
pub use boot_loader_name::BootLoaderNameTag;
4445
pub use command_line::CommandLineTag;
4546
pub use efi::{EFIImageHandle32, EFIImageHandle64, EFISdt32, EFISdt64};
@@ -244,8 +245,9 @@ impl BootInformation {
244245
.map(|tag| unsafe { &*(tag as *const Tag as *const CommandLineTag) })
245246
}
246247

247-
/// Search for the VBE framebuffer tag.
248-
pub fn framebuffer_tag(&self) -> Option<FramebufferTag> {
248+
/// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the
249+
/// framebuffer type is unknown, while the framebuffer tag is present.
250+
pub fn framebuffer_tag(&self) -> Option<Result<FramebufferTag, UnknownFramebufferType>> {
249251
self.get_tag(TagType::Framebuffer)
250252
.map(framebuffer::framebuffer_tag)
251253
}
@@ -305,7 +307,7 @@ impl BootInformation {
305307
}
306308

307309
/// Search for the VBE information tag.
308-
pub fn vbe_info_tag(&self) -> Option<&'static VBEInfoTag> {
310+
pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> {
309311
self.get_tag(TagType::Vbe)
310312
.map(|tag| unsafe { &*(tag as *const Tag as *const VBEInfoTag) })
311313
}
@@ -588,7 +590,7 @@ mod tests {
588590
use framebuffer::{FramebufferField, FramebufferTag, FramebufferType};
589591
assert_eq!(
590592
bi.framebuffer_tag(),
591-
Some(FramebufferTag {
593+
Some(Ok(FramebufferTag {
592594
address: 4244635648,
593595
pitch: 5120,
594596
width: 1280,
@@ -608,7 +610,7 @@ mod tests {
608610
size: 8
609611
}
610612
}
611-
})
613+
}))
612614
)
613615
}
614616

@@ -643,7 +645,10 @@ mod tests {
643645
assert_eq!(bytes.0.len(), bi.total_size());
644646
use framebuffer::{FramebufferColor, FramebufferType};
645647
assert!(bi.framebuffer_tag().is_some());
646-
let fbi = bi.framebuffer_tag().unwrap();
648+
let fbi = bi
649+
.framebuffer_tag()
650+
.expect("Framebuffer info should be available")
651+
.expect("Framebuffer info type should be valid");
647652
assert_eq!(fbi.address, 4244635648);
648653
assert_eq!(fbi.pitch, 5120);
649654
assert_eq!(fbi.width, 1280);
@@ -1255,7 +1260,10 @@ mod tests {
12551260
);
12561261

12571262
// Test the Framebuffer tag
1258-
let fbi = bi.framebuffer_tag().unwrap();
1263+
let fbi = bi
1264+
.framebuffer_tag()
1265+
.expect("Framebuffer info should be available")
1266+
.expect("Framebuffer info type should be valid");
12591267
assert_eq!(fbi.address, 753664);
12601268
assert_eq!(fbi.pitch, 160);
12611269
assert_eq!(fbi.width, 80);

0 commit comments

Comments
 (0)