Skip to content

add raw_type! macro for ABI-safe raw newtypes - #314

Merged
phip1611 merged 16 commits into
mainfrom
fix/open-enum-newtypes
Aug 26, 2026
Merged

add raw_type! macro for ABI-safe raw newtypes#314
phip1611 merged 16 commits into
mainfrom
fix/open-enum-newtypes

Conversation

@phip1611

Copy link
Copy Markdown
Member

UB-free safe parsing of enums and bitflags from binary data structures via a new raw_type! macro.

Comment thread multiboot2-common/src/lib.rs
Comment thread multiboot2-common/src/raw.rs Outdated
Comment thread multiboot2-common/src/raw.rs Outdated
Comment thread multiboot2-common/src/raw.rs Outdated
Comment thread multiboot2-common/src/raw.rs Outdated
Comment thread multiboot2/CHANGELOG.md Outdated
Comment thread multiboot2/src/vbe_info.rs Outdated
Comment thread multiboot2-header/src/header.rs
Comment thread multiboot2-header/src/builder.rs Outdated
Comment thread multiboot2-header/src/console.rs Outdated
@phip1611
phip1611 force-pushed the fix/open-enum-newtypes branch from b9fd642 to 4c96a1f Compare August 25, 2026 16:03

@phip1611 phip1611 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Getting closer. Please make sure to run all CI test also locally (e.g. rustdoc fails).

Comment thread multiboot2/src/tag_type.rs Outdated
Comment thread multiboot2/src/tag_type.rs Outdated
Comment thread multiboot2/src/vbe_info.rs Outdated
@phip1611
phip1611 force-pushed the fix/open-enum-newtypes branch 3 times, most recently from c1c7c34 to 8d03ffb Compare August 26, 2026 05:43
Comment thread multiboot2-common/src/raw.rs Outdated
/// The newtype must debug-print the semantic of its value.
#[test]
fn test_debug() {
assert_eq!(format!("{:?}", TestRaw::new(0)), "Foo");

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Debug should always be Foo(x) where x is the known discriminant. Display impl should be Foo for known variants and Custom(x) for the custom value.

Comment thread multiboot2-header/src/console.rs Outdated
pub struct ConsoleHeaderTagFlagsRaw(u32);

/// Possible flags for [`ConsoleHeaderTag`].
/// The console flags of the [`ConsoleHeaderTag`]. The values are taken

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

"The values are take from the example C Code"... remove! The crate is spec compliant so this is clear. Also make sure you do not use such doc elsewhere.

Comment thread multiboot2-header/src/tags.rs Outdated
/// representation (`u32`). This value stands in the `arch` property of
/// [`crate::Multiboot2BasicHeader`].
/// ABI compatible representation of the ISA/ARCH of a Multiboot2 header,
/// matching the binary representation (`u32`). This value stands in the

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

A single sentence introducing the type, then a newline, then a new paragraph. also in other doc comments you are touching.

Several #[repr(C)] structures across the workspace store fields typed
as Rust enums with a restricted set of valid discriminants. As these
structures are parsed from raw bootloader-provided memory, any unknown
value is undefined behavior. The macro generates the fix once: a
#[repr(transparent)] newtype (Raw suffix) for which every bit pattern
is valid, a high-level open-set enum with a generated Custom fallback
variant, and all conversions between newtype, enum, and integer.
Renames the hand-written newtype to follow the new Raw-suffix
convention of the macro. No behavioral change. The MbiTagTypeId
re-export of multiboot2-header follows the rename to MbiTagTypeRaw so
that every commit builds.
Also lets MemoryArea::typ() return the high-level MemoryAreaType and
MemoryArea::new() take impl Into<MemoryAreaType>. The newtype becomes
#[repr(transparent)] instead of #[repr(C)] (identical layout).
new() now takes &[MbiTagType] and requests() returns an iterator over
MbiTagType; the raw representation stays an internal storage detail.
Follows the rename of MbiTagTypeId to MbiTagTypeRaw.
The type field was a Rust enum with three valid discriminants, so
parsing a tag with an unknown framebuffer type was undefined behavior.
The field now stores the FramebufferTypeRaw newtype generated by
raw_type!; the open-set FramebufferKind enum drives the dispatch in
buffer_type(), which reports unknown values as UnknownFramebufferType,
as before.
The memory_model field was a Rust enum with eight valid discriminants,
while the VBE spec reserves 0x08..=0x0F and assigns 0x10..=0xFF to the
OEM. The field is now typed as the VBEMemoryModelRaw newtype generated
by raw_type!; VBEMemoryModel gained a Custom variant.
The tag type was a Rust enum with eleven valid discriminants and is
read for every tag during iteration of raw memory, so any custom or
future tag type was undefined behavior. HeaderTagHeader now stores the
HeaderTagTypeRaw newtype generated by raw_type!; the typ() getters
keep returning HeaderTagType, which gained a Custom variant. The
unused HeaderTagType::count() was removed.
The arch field was the HeaderTagISA enum with two valid discriminants
(0 and 4), so loading an image with any other architecture value was
undefined behavior. The field now stores the HeaderTagISARaw newtype
generated by raw_type!; the arch() getters keep returning
HeaderTagISA, which gained a Custom variant.
The preference field was a Rust enum with three valid discriminants,
so parsing a tag with any other value was undefined behavior. The tag
now stores the RelocatableHeaderTagPreferenceRaw newtype generated by
raw_type!; preference() keeps returning the high-level enum, which
gained a Custom variant.
The flags field of every header tag was the HeaderTagFlag enum with
two valid discriminants, so parsing a tag with any other value was
undefined behavior. HeaderTagHeader now stores the HeaderTagFlagRaw
newtype generated by raw_type!; the flags() getters keep returning
HeaderTagFlag, which gained a Custom variant.
The enum discriminants did not match the specification: per the
example C code, console-required is value 1 and EGA text support is
value 2, but the enum serialized them as 0 and 1. A bootloader
therefore read a ConsoleRequired tag as "no console required", and
parsing any other value was undefined behavior. The tag now stores the
ConsoleHeaderTagFlagsRaw newtype generated by raw_type!, and the enum
carries the spec-mandated values plus a Custom variant.
Replaces the test_assert_size()/test_layout()-style unit tests with
const assertions placed right after the respective type definitions.
This checks the layout on every build instead of only when running the
test suite.
Raw types start with "ABI compatible representation of ..."; the
high-level enums first explain what the type is and then reference the
raw type they abstract.
Raw types consistently start with "ABI compatible representation of
..."; the high-level enums first explain what the type is according to
the spec and then reference the raw type they abstract.
…enums

Raw types consistently start with "ABI compatible representation of
..."; the high-level enums first explain what the type is according to
the spec and then reference the raw type they abstract.
@phip1611
phip1611 force-pushed the fix/open-enum-newtypes branch from c06fcbf to 0b197e3 Compare August 26, 2026 06:16
@phip1611
phip1611 added this pull request to the merge queue Aug 26, 2026
Merged via the queue into main with commit a3b7702 Aug 26, 2026
29 checks passed
@phip1611
phip1611 deleted the fix/open-enum-newtypes branch August 26, 2026 08:24
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.

1 participant