Skip to content

Commit d2d8932

Browse files
committed
Make the MADT !Unpin to prevent unsoundness
This prevents an `Madt` being moved away from its following, but not represented, dynamic entries. This would previously have caused unsoundness as arbitrary memory would be read from after wherever the `Madt` had ended up. By prevening the user from getting anything other than a `Pin<&Madt>`, this is prevented.
1 parent 37d4bb2 commit d2d8932

File tree

1 file changed

+21
-10
lines changed

1 file changed

+21
-10
lines changed

acpi/src/madt.rs

+21-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ use crate::{
33
AcpiTable,
44
};
55
use bit_field::BitField;
6-
use core::{marker::PhantomData, mem};
6+
use core::{
7+
marker::{PhantomData, PhantomPinned},
8+
mem,
9+
pin::Pin,
10+
};
711

812
#[cfg(feature = "allocator_api")]
913
use crate::{
@@ -27,16 +31,22 @@ pub enum MadtError {
2731
/// to read each entry from it.
2832
///
2933
/// In modern versions of ACPI, the MADT can detail one of four interrupt models:
30-
/// * The ancient dual-i8259 legacy PIC model
31-
/// * The Advanced Programmable Interrupt Controller (APIC) model
32-
/// * The Streamlined Advanced Programmable Interrupt Controller (SAPIC) model (for Itanium systems)
33-
/// * The Generic Interrupt Controller (GIC) model (for ARM systems)
34+
/// - The ancient dual-i8259 legacy PIC model
35+
/// - The Advanced Programmable Interrupt Controller (APIC) model
36+
/// - The Streamlined Advanced Programmable Interrupt Controller (SAPIC) model (for Itanium systems)
37+
/// - The Generic Interrupt Controller (GIC) model (for ARM systems)
38+
///
39+
/// The MADT is a variable-sized structure consisting of a static header and then a variable number of entries.
40+
/// This type only contains the static portion, and then uses pointer arithmetic to parse the following entries.
41+
/// To make this sound, this type is `!Unpin` - this prevents you from getting anything other than a `Pin<&Madt>`
42+
/// out of a `PhysicalMapping`, and so prevents a `Madt` from being moved before [`Madt::entries`] is called.
3443
#[repr(C, packed)]
35-
#[derive(Debug, Clone, Copy)]
44+
#[derive(Debug)]
3645
pub struct Madt {
3746
pub header: SdtHeader,
3847
pub local_apic_address: u32,
3948
pub flags: u32,
49+
_pinned: PhantomPinned,
4050
}
4151

4252
/// ### Safety: Implementation properly represents a valid MADT.
@@ -51,7 +61,7 @@ unsafe impl AcpiTable for Madt {
5161
impl Madt {
5262
#[cfg(feature = "allocator_api")]
5363
pub fn parse_interrupt_model_in<'a, A>(
54-
&self,
64+
self: Pin<&Self>,
5565
allocator: A,
5666
) -> AcpiResult<(InterruptModel<'a, A>, Option<ProcessorInfo<'a, A>>)>
5767
where
@@ -96,7 +106,7 @@ impl Madt {
96106

97107
#[cfg(feature = "allocator_api")]
98108
fn parse_apic_model_in<'a, A>(
99-
&self,
109+
self: Pin<&Self>,
100110
allocator: A,
101111
) -> AcpiResult<(InterruptModel<'a, A>, Option<ProcessorInfo<'a, A>>)>
102112
where
@@ -301,9 +311,10 @@ impl Madt {
301311
))
302312
}
303313

304-
pub fn entries(&self) -> MadtEntryIter {
314+
pub fn entries(self: Pin<&Self>) -> MadtEntryIter<'_> {
315+
let ptr = unsafe { Pin::into_inner_unchecked(self) as *const Madt as *const u8 };
305316
MadtEntryIter {
306-
pointer: unsafe { (self as *const Madt as *const u8).add(mem::size_of::<Madt>()) },
317+
pointer: unsafe { ptr.add(mem::size_of::<Madt>()) },
307318
remaining_length: self.header.length - mem::size_of::<Madt>() as u32,
308319
_phantom: PhantomData,
309320
}

0 commit comments

Comments
 (0)