Skip to content

Check table version before calling UEFI 2.0+ functions #436

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

## uefi - [Unreleased]

### Added

- Added EFI revision constants to `Revision`.

### Fixed

- The `BootServices::create_event_ex` and
`RuntimeServices::query_variable_info` methods now check the table
version to make sure it's 2.0 or higher before calling the associated
function pointers. This prevents potential invalid pointer access.

## uefi-macros - [Unreleased]

## uefi-services - [Unreleased]
Expand Down Expand Up @@ -32,6 +43,7 @@
- `DevicePath` is now a DST that represents an entire device path. The
`DevicePathInstance` and `DevicePathNode` provide views of path
instances and nodes, respectively.
- The methods of `Revision` are now `const`.

### Fixed

Expand Down
9 changes: 8 additions & 1 deletion src/table/boot.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! UEFI services available during boot.

use super::Header;
use super::{Header, Revision};
use crate::data_types::Align;
use crate::proto::device_path::{DevicePath, FfiDevicePath};
#[cfg(feature = "exts")]
Expand Down Expand Up @@ -437,6 +437,9 @@ impl BootServices {
/// More than one event of type `EventType::TIMER` may be part of a single event group. However,
/// there is no mechanism for determining which of the timers was signaled.
///
/// This operation is only supported starting with UEFI 2.0; earlier
/// versions will fail with [`Status::UNSUPPORTED`].
///
/// # Safety
///
/// The caller must ensure they are passing a valid `Guid` as `event_group`, if applicable.
Expand All @@ -448,6 +451,10 @@ impl BootServices {
notify_ctx: Option<NonNull<c_void>>,
event_group: Option<NonNull<Guid>>,
) -> Result<Event> {
if self.header.revision < Revision::EFI_2_00 {
return Err(Status::UNSUPPORTED.into());
}

let mut event = MaybeUninit::<Event>::uninit();

(self.create_event_ex)(
Expand Down
43 changes: 39 additions & 4 deletions src/table/revision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,41 @@ use core::fmt;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Revision(u32);

// Allow missing docs, there's nothing useful to document about these
// constants.
#[allow(missing_docs)]
impl Revision {
pub const EFI_1_02: Self = Self::new(1, 2);
pub const EFI_1_10: Self = Self::new(1, 10);
pub const EFI_2_00: Self = Self::new(2, 00);
pub const EFI_2_10: Self = Self::new(2, 10);
pub const EFI_2_20: Self = Self::new(2, 20);
pub const EFI_2_30: Self = Self::new(2, 30);
pub const EFI_2_31: Self = Self::new(2, 31);
pub const EFI_2_40: Self = Self::new(2, 40);
pub const EFI_2_50: Self = Self::new(2, 50);
pub const EFI_2_60: Self = Self::new(2, 60);
pub const EFI_2_70: Self = Self::new(2, 70);
pub const EFI_2_80: Self = Self::new(2, 80);
pub const EFI_2_90: Self = Self::new(2, 90);
}

impl Revision {
/// Creates a new revision.
pub fn new(major: u16, minor: u16) -> Self {
let (major, minor) = (u32::from(major), u32::from(minor));
pub const fn new(major: u16, minor: u16) -> Self {
let major = major as u32;
let minor = minor as u32;
let value = (major << 16) | minor;
Revision(value)
}

/// Returns the major revision.
pub fn major(self) -> u16 {
pub const fn major(self) -> u16 {
(self.0 >> 16) as u16
}

/// Returns the minor revision.
pub fn minor(self) -> u16 {
pub const fn minor(self) -> u16 {
self.0 as u16
}
}
Expand All @@ -35,3 +55,18 @@ impl fmt::Debug for Revision {
write!(f, "{}.{}.{}", major, minor / 10, minor % 10)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_revision() {
let rev = Revision::EFI_2_31;
assert_eq!(rev.major(), 2);
assert_eq!(rev.minor(), 31);
assert_eq!(rev.0, 0x0002_001f);

assert!(Revision::EFI_1_10 < Revision::EFI_2_00);
}
}
9 changes: 8 additions & 1 deletion src/table/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! UEFI services available at runtime, even after the OS boots.

use super::Header;
use super::{Header, Revision};
#[cfg(feature = "exts")]
use crate::data_types::FromSliceWithNulError;
use crate::result::Error;
Expand Down Expand Up @@ -248,11 +248,18 @@ impl RuntimeServices {
/// Get information about UEFI variable storage space for the type
/// of variable specified in `attributes`.
///
/// This operation is only supported starting with UEFI 2.0; earlier
/// versions will fail with [`Status::UNSUPPORTED`].
///
/// See [`VariableStorageInfo`] for details of the information returned.
pub fn query_variable_info(
&self,
attributes: VariableAttributes,
) -> Result<VariableStorageInfo> {
if self.header.revision < Revision::EFI_2_00 {
return Err(Status::UNSUPPORTED.into());
}

let mut info = VariableStorageInfo::default();
unsafe {
(self.query_variable_info)(
Expand Down