Skip to content
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

Add unsafe_protocol macro and drop use of the unstable negative_impls feature #607

Merged
merged 7 commits into from
Dec 27, 2022

Conversation

nicholasbishop
Copy link
Member

Our protocols are all marked !Send and !Sync using the unstable negative_impls feature. There's another way to mark things !Send and !Sync though: add a field that itself is !Send and !Sync, for example a raw pointer. We don't want to actually change the struct layout of course, but we can use the PhantomData type for this.

The new unsafe_protocol macro implements this idea; it's an attribute macro that combines the functionality of the unsafe_guid attribute macro and the Protocol derive macro, as well as adding a hidden core::marker::PhantomData<*const u8> field for !Send and !Sync.

The real purpose of this change is to remove use of the unstable negative_impls feature towards our goal of building on the stable channel. But it also ends up with a slightly nicer API as well.

Short log:

  • uefi-macros: Refactor GUID tests
  • uefi: Remove non-protocol uses of unsafe_guid
  • uefi-macros: Add unsafe_protocol macro
  • uefi: Use unsafe_protocol! for all protocol impls
  • uefi-macros: Drop the unsafe_guid and Protocol macros
  • Drop use of unstable negative_impls feature
  • Update changelog

Tracking issue for unstable features: #452

Checklist

  • Sensible git history (for example, squash "typo" or "fix" commits). See the Rewriting History guide for help.
  • Update the changelog (if necessary)

@@ -80,8 +80,7 @@ fn test_watchdog(bt: &BootServices) {
}

/// Dummy protocol for tests
#[unsafe_guid("1a972918-3f69-4b5d-8cb4-cece2309c7f5")]
#[derive(Protocol)]
#[unsafe_protocol("1a972918-3f69-4b5d-8cb4-cece2309c7f5")]
Copy link
Contributor

@phip1611 phip1611 Dec 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

general question: why does it need to be called unsafe, tho? Why can't the attribute be called uefi_protocol("...")? I do not see a benefit of the prefix unsafe here and it only adds confusion.

What do you think?

@nicholasbishop

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's called unsafe_protocol my question would be what makes it unsafe / how do I use it safely?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's prefixed with unsafe_ because passing in the wrong GUID can easily lead to unsoundness.

Here's a quick example of how bad things can happen:

The definition of EFI_DISK_IO_PROTOCOL from the spec is:

#define EFI_DISK_IO_PROTOCOL_GUID \
    {0xCE345171,0xBA0B,0x11d2,\
    {0x8e,0x4F,0x00,0xa0,0xc9,0x69,0x72,0x3b}}

typedef struct _EFI_DISK_IO_PROTOCOL {
    UINT64 Revision;
    EFI_DISK_READ ReadDisk;
    EFI_DISK_WRITE WriteDisk;
} EFI_DISK_IO_PROTOCOL;

Now consider this Rust code:

#[repr(C)]
// Bad!
#[unsafe_protocol("ce345171-ba0b-11d2-8e4f-00a0c969723b")]
pub struct MyCoolType {
    do_it: extern "efiapi" fn(),
}

let handle = bt.get_handle_for_protocol::<MyCoolType>().unwrap();
let c = bt.open_protocol_exclusive::<MyCoolType>(handle).unwrap();
(c.do_it)();

I've defined a protocol called MyCoolType with the same GUID as EFI_DISK_IO_PROTOCOL. When I call get_handle_for_protocol and open_protocol_exclusive, UEFI will think it's looking for a disk handle and opening the disk IO protocol. So those calls will succeed, and although c will be a ScopedProtocol<MyCoolType>, it will contain completely invalid data.

The docstring has a # Safety section: https://github.com/rust-osdev/uefi-rs/pull/607/files#diff-ff7f936d7319fc633630baca626659855f359b7185c91237bc9d68bd798256aeR33. There's not much too it; you have to make sure the type and GUID are correct and match up. Definitely open to improving or expanding that paragraph if you have suggestions though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, thanks for the clarification.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@@ -142,7 +142,6 @@ pub enum FileInfoCreationError {
/// attribute. Other changes must be carried out in a separate transaction.
#[derive(Debug, Eq, PartialEq)]
#[repr(C)]
#[unsafe_guid("09576e92-6d3f-11d2-8e39-00a0c969723b")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! thanks for fixing this

@phip1611
Copy link
Contributor

phip1611 commented Dec 20, 2022

Awesome work @nicholasbishop, thanks!

Switch the tests from using `unsafe_guid!` to `guid!`. Both use the same
internal code, so we're testing more or less the same thing, but `guid!`
is a little more direct.

Also, as we previously did with the entry macro, split the GUID tests
into multiple files to make it easier to visually inspect the error
output.

The "good" test case has been removed since that's already covered in
plenty of places elsewhere.
The `unsafe_guid` macro is almost always used for creating
protocols. There are three other uses though, for identifying file info
types. Replace these with explicit impls of the `Identify` trait. Now
that we have a `guid!` macro, this doesn't reduce the clarity of the
code at all.
This attribute macro combines the functionality of the `unsafe_guid`
attribute macro and the `Protocol` derive macro. It impls the `Protocol`
and `Identify` traits, and it also marks the type as `!Send` and
`!Sync`.

This provides a slightly nicer API for protocols (`unsafe_protocol` is a
clearer name than `unsafe_guid`, and only one macro is needed instead of
two), but more importantly it does the `!Send` and `!Sync` part via a
hidden `PhantomData` field rather than relying on the unstable
`negative_impls` feature.
Switch all uses of `unsafe_guid!` + `derive(Protocol)` to using
`unsafe_protocol!`.

Also update the docstrings for the `Identify` and `Protocol` traits.
These are no longer used anywhere.
This is no longer used anywhere.
@phip1611 phip1611 merged commit 93699b8 into rust-osdev:main Dec 27, 2022
@nicholasbishop nicholasbishop deleted the bishop-protocol-macro-3 branch December 28, 2022 04:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants