Skip to content

Commit 245258b

Browse files
rcercIsaacWoods
authored andcommitted
acpi: Map a table entirely before attempting to validate it
This ensures reads from locations in the table after the header during validation return correct values and cause no page faults.
1 parent 8e71a96 commit 245258b

File tree

1 file changed

+69
-43
lines changed

1 file changed

+69
-43
lines changed

acpi/src/lib.rs

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -170,40 +170,64 @@ where
170170
///
171171
/// ### Safety: Caller must ensure that the provided mapping is a fully validated RSDP.
172172
pub unsafe fn from_validated_rsdp(handler: H, rsdp_mapping: PhysicalMapping<H, Rsdp>) -> AcpiResult<Self> {
173-
let revision = rsdp_mapping.revision();
173+
macro_rules! read_root_table {
174+
($signature_name:ident, $address_getter:ident) => {{
175+
#[repr(transparent)]
176+
struct RootTable {
177+
header: SdtHeader,
178+
}
179+
180+
unsafe impl AcpiTable for RootTable {
181+
const SIGNATURE: Signature = Signature::$signature_name;
182+
183+
fn header(&self) -> &SdtHeader {
184+
&self.header
185+
}
186+
}
187+
188+
// Unmap RSDP as soon as possible
189+
let table_phys_start = rsdp_mapping.$address_getter() as usize;
190+
drop(rsdp_mapping);
191+
192+
// Map and validate root table
193+
// SAFETY: Addresses from a validated `RSDP` are also guaranteed to be valid.
194+
let table_mapping = unsafe { read_table::<_, RootTable>(handler.clone(), table_phys_start) }?;
195+
196+
// Convert `table_mapping` to header mapping for storage
197+
// Avoid requesting table unmap twice (from both original and converted `table_mapping`s)
198+
let table_mapping = mem::ManuallyDrop::new(table_mapping);
199+
// SAFETY: `SdtHeader` is equivalent to `Sdt` memory-wise
200+
let table_mapping = unsafe {
201+
PhysicalMapping::new(
202+
table_mapping.physical_start(),
203+
table_mapping.virtual_start().cast::<SdtHeader>(),
204+
table_mapping.region_length(),
205+
table_mapping.mapped_length(),
206+
handler.clone(),
207+
)
208+
};
209+
210+
table_mapping
211+
}};
212+
}
174213

175-
if revision == 0 {
214+
let revision = rsdp_mapping.revision();
215+
let root_table_mapping = if revision == 0 {
176216
/*
177217
* We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
178218
*/
179219

180-
// Safety: Addresses from a validated `RSDP` are also guaranteed to be valid.
181-
let rsdt_mapping: PhysicalMapping<H, SdtHeader> = unsafe {
182-
handler.map_physical_region::<SdtHeader>(
183-
rsdp_mapping.rsdt_address() as usize,
184-
core::mem::size_of::<SdtHeader>(),
185-
)
186-
};
187-
rsdt_mapping.validate(Signature::RSDT)?;
188-
189-
Ok(Self { mapping: rsdt_mapping, revision, handler })
220+
read_root_table!(RSDT, rsdt_address)
190221
} else {
191222
/*
192223
* We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
193224
* to 32 bits on x86.
194225
*/
195226

196-
// Safety: Addresses from a validated `RSDP` are also guaranteed to be valid.
197-
let xsdt_mapping: PhysicalMapping<H, SdtHeader> = unsafe {
198-
handler.map_physical_region::<SdtHeader>(
199-
rsdp_mapping.xsdt_address() as usize,
200-
core::mem::size_of::<SdtHeader>(),
201-
)
202-
};
203-
xsdt_mapping.validate(Signature::XSDT)?;
227+
read_root_table!(XSDT, xsdt_address)
228+
};
204229

205-
Ok(Self { mapping: xsdt_mapping, revision, handler })
206-
}
230+
Ok(Self { mapping: root_table_mapping, revision, handler })
207231
}
208232

209233
/// The ACPI revision of the tables enumerated by this structure.
@@ -333,35 +357,37 @@ unsafe fn read_table<H: AcpiHandler, T: AcpiTable>(
333357
address: usize,
334358
) -> AcpiResult<PhysicalMapping<H, T>> {
335359
// Attempt to peek at the SDT header to correctly enumerate the entire table.
336-
// Safety: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a software issue).
337-
let mapping = unsafe { handler.map_physical_region::<SdtHeader>(address, core::mem::size_of::<SdtHeader>()) };
338-
mapping.validate(T::SIGNATURE)?;
360+
// SAFETY: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a software issue).
361+
let header_mapping = unsafe { handler.map_physical_region::<SdtHeader>(address, mem::size_of::<SdtHeader>()) };
339362

340-
// If possible (if the existing mapping covers enough memory), resuse the existing physical mapping.
363+
// If possible (if the existing mapping covers enough memory), reuse the existing physical mapping.
341364
// This allows allocators/memory managers that map in chunks larger than `size_of::<SdtHeader>()` to be used more efficiently.
342-
if mapping.mapped_length() >= (mapping.length as usize) {
343-
// Safety: Pointer is known non-null.
344-
let virtual_start =
345-
unsafe { core::ptr::NonNull::new_unchecked(mapping.virtual_start().as_ptr() as *mut _) };
365+
let table_length = header_mapping.length as usize;
366+
let table_mapping = if header_mapping.mapped_length() >= table_length {
367+
// Avoid requesting table unmap twice (from both `header_mapping` and `table_mapping`)
368+
let header_mapping = mem::ManuallyDrop::new(header_mapping);
346369

347-
// Safety: Mapping is known-good and validated.
348-
Ok(unsafe {
370+
// SAFETY: `header_mapping` maps entire table.
371+
unsafe {
349372
PhysicalMapping::new(
350-
mapping.physical_start(),
351-
virtual_start,
352-
mapping.region_length(),
353-
mapping.mapped_length(),
354-
handler.clone(),
373+
header_mapping.physical_start(),
374+
header_mapping.virtual_start().cast::<T>(),
375+
table_length,
376+
header_mapping.mapped_length(),
377+
handler,
355378
)
356-
})
379+
}
357380
} else {
358-
let sdt_length = mapping.length;
359381
// Drop the old mapping here, to ensure it's unmapped in software before requesting an overlapping mapping.
360-
drop(mapping);
382+
drop(header_mapping);
361383

362-
// Safety: Address and length are already known-good.
363-
Ok(unsafe { handler.map_physical_region(address, sdt_length as usize) })
364-
}
384+
// SAFETY: Address and length are already known-good.
385+
unsafe { handler.map_physical_region(address, table_length) }
386+
};
387+
388+
table_mapping.validate()?;
389+
390+
Ok(table_mapping)
365391
}
366392

367393
/// Iterator that steps through all of the tables, and returns only the SSDTs as `AmlTable`s.

0 commit comments

Comments
 (0)