Skip to content

nvx-rs: add a strict GPT reader for layered root disks - #673

Merged
Pedro Henrique Penna (ppenna) merged 3 commits into
devfrom
esaurez/a3-gpt-validation-20261020
Oct 22, 2026
Merged

Pedro Henrique Penna (ppenna) merged 3 commits into
devfrom
esaurez/a3-gpt-validation-20261020

Conversation

@esaurez

@esaurez Enrique Saurez (esaurez) commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Adds nvx::gpt, a feature-free reader of the GPT disks that hold a layered root: p1, whose bytes configure an adapter, and the read-only layers of the root in the partitions after it. Its GUIDs are nvx::uuid::Uuids, the crate's one UUID type. This is the first part of GPT support in the guest library. The guest's device validation, which asks the kernel to reread the partition table and confirms that the kernel's partitions are exactly the ones read here, follows in a stacked pull request; mounting the layers comes after that.

What changed

  • Disk::read reads the protective MBR, the primary GPT header, and the primary partition entry array through a ReadAt source that the caller gives (bytes in memory, a file, or, in the guest, a block device), with the logical block size and disk size that the caller states. It checks them strictly, as UEFI 2.10 specifies them and as Linux's own GPT reader requires them, so that a disk that Linux would not read as this table fails early and clearly:
    • Protective MBR: the signature, and exactly one 0xEE record, starting at LBA 1, with the other three records zero, so a classic or hybrid MBR fails.
    • Header: signature, revision 1.0, a size from 92 bytes through one block, the CRC-32, the zero reserved field, MyLBA, and the layout 2 <= entry LBA, entry LBA + entry blocks <= first usable <= last usable < alternate LBA <= last LBA.
    • Entry array: 128-byte entries, the only size that Linux reads, at most 1 MiB of them, allocated only once the header has bounded the array, and the array's CRC-32.
    • Partitions: the used entries come first, without holes, and number 2 through 255: p1 and 1 to 254 layers, since Linux creates partitions 1 through 255 only. Each lies in the usable blocks, overlaps no other, and has a unique GUID that is not nil and repeats neither the disk GUID nor another partition's.
  • The backup header and array are never read, so a disk whose primary table is invalid fails even when its backup is intact. Linux, too, falls back to the backup only when its command line forces GPT.
  • Arithmetic is checked throughout, in the disk's logical blocks.
  • Expected adds what a caller knows: the disk GUID, such as the one that a boot parameter names, and the count, unique GUIDs, and sizes of the partitions. Each expectation is optional and is checked after the format's own checks.
  • Disk::read_p1 returns p1's bytes, opaque and not necessarily UTF-8, without trailing NUL bytes and ASCII whitespace. A p1 larger than the caller's bound fails before any of it is read, and p1 is never probed as a filesystem.
  • GptError names the check that failed; apart from failed reads, the checks run in the order of its variants.
  • The module depends on nothing outside the standard library (it computes the CRC-32 from a table that the compiler builds), so the guest and the host's checks of the images that it stages can share it. The crate README lists it.
  • GUIDs are nvx::uuid::Uuids, so that a boot parameter's disk GUID, a disk's GUIDs, and a filesystem's UUID compare as one type, without a conversion through text. Uuid::from_bytes_le and to_bytes_le convert the layout in which GPT, like other EFI GUIDs, stores the first three groups little-endian.
Limit Value
Logical block size A power of two from 512 through 65,536 bytes, Linux's range
Entry size 128 bytes
Entry array 1 through 8,192 entries, at most 1 MiB, allocated after the header bounds it
Partitions 2 through 255 used entries, contiguous from the first
Reads 512 bytes of LBA 0, one block for the header, the entry array, and p1 up to the caller's bound

Part of #621. Refs #622 and #535.

Validation

At this head, rebased onto dev at e714acb, on Windows 11 (MSVC, Rust 1.95.0) and on WSL Ubuntu as a non-root user (Rust 1.95.0), in nvx-rs with NVX_BUNDLE=skip:

  • cargo fmt --all --check
  • cargo clippy --locked --all-targets --all-features -- -D warnings
  • cargo clippy --locked --lib --no-default-features -- -D warnings
  • cargo test --locked --all-features, then cargo test --locked. The 30 new GPT tests write crafted disks of 512- and 4096-byte blocks field by field: every header and entry-array field, CRC failures, the protective MBR (classic, hybrid, and misplaced records), overflowing LBAs and sizes, overlaps, holes, 255 and 256 partitions, a corrupt primary table beside an intact backup, a source that records every read, mismatched expected identities and sizes, and p1 in one sector, with bytes that are not UTF-8, with NUL and whitespace padding, and over its bound.
  • cargo doc --locked --no-deps --all-features with RUSTDOCFLAGS=-D warnings
  • cargo +1.89 check --locked --all-targets --all-features, the minimum supported Rust version
  • tests/runtime-adapter in its protocol-only and host-only configurations: rustfmt, Clippy, build, tests, and the 1.89 check
  • aci_edge_sandboxes, as its CI checks it, with ACI_EDGE_SANDBOXES_AGENT_BUNDLE=skip: rustfmt, Clippy, tests, nvx.py test-agent-bundling, rustdoc, and its minimum supported Rust version

On WSL Ubuntu only:

  • cargo check --locked --all-targets --all-features --target aarch64-apple-darwin, in nvx-rs and in aci_edge_sandboxes
  • python3 scripts/nvx.py check-guest-binaries, build-guest-binaries --output DIR, and test-guest-binaries --binaries DIR. The nine guest binaries are byte-identical to those built from dev at e714acb.
  • tests/runtime-adapter in its musl guest-only configuration

And python scripts/nvx.py verify and python scripts/check_markdown_links.py on Windows.

This pull request changes no guest image, binary, or script, so I ran no microVM scenario for it; the stacked device validation pull request adds a GPT scenario and runs it on real VMs. CI runs the hypervisor matrix.

Copilot AI balanced review requested due to automatic review settings October 21, 2026 03:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

Static review found no blocking issues in the bounded parser or supplied test coverage; automated checks were not rerun.

No open issues 🎉

What changed in this PR

Adds a feature-free GPT reader shared by host and guest code, laying the groundwork for layered-root disk support in #621.

Changes:

  • Validates primary GPT metadata, partition identities, and extents with bounded reads.
  • Adds optional expectations and bounded, opaque p1 configuration reads.
  • Adds crafted-disk tests and public API documentation.
File Description
nvx-rs/​src/​lib.rs Exports and documents the GPT module.
nvx-rs/​src/​gpt/​tests.rs Tests validation, boundaries, sources, and p1 reads.
nvx-rs/​src/​gpt/​source.rs Provides memory and file read adapters.
nvx-rs/​src/​gpt/​mod.rs Implements parsing, validation, and p1 reads.
nvx-rs/​src/​gpt/​guid.rs Adds GPT GUID parsing and formatting.
nvx-rs/​src/​gpt/​error.rs Defines validation errors and diagnostics.
nvx-rs/​src/​gpt/​crc.rs Implements and tests CRC-32.
nvx-rs/​README.md Documents the module and feature-free availability.

Review effort: Balanced

Comment thread nvx-rs/src/gpt/guid.rs Outdated
Comment thread nvx-rs/src/gpt/crc.rs
Copilot AI balanced review requested due to automatic review settings October 22, 2026 03:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The additions are self-contained, have targeted validation coverage, and have no identified blocking defects in static review.

No open issues 🎉

Review effort: Balanced

Enrique Saurez and others added 3 commits October 21, 2026 21:22
Add nvx::gpt, a reader of the GPT disks that hold a layered root: p1,
whose bytes configure an adapter, and the read-only layers of the root
in the partitions after it. Disk::read checks the protective MBR, the
primary header, and the primary partition entry array as UEFI specifies
them and as Linux's own GPT reader requires them, so that a disk that
Linux would not read as this table fails early and clearly. It never
reads the backup, allocates the entry array only once the header has
bounded it, and checks its arithmetic in the disk's logical blocks.

Expected adds the disk GUID and the partitions' unique GUIDs and sizes
that a caller knows, and Disk::read_p1 returns p1's opaque bytes, up to
the caller's bound and without trailing NUL bytes and whitespace.

The module depends on nothing outside the standard library, so the
guest's device validation and the host's checks of the images that it
stages can share it.

Part of #621.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1b4b569c-a481-43aa-bfd8-c14a9762c371
The crate meets UUIDs in several places: the GUIDs of a GPT disk and
its partitions, the UUIDs of layers' filesystems, and the identities
that a boot parameter names, which a guest compares with what a disk
holds. Read the GPT reader's GUIDs as nvx::uuid::Uuid, the type that
the boot tokens of a layered root already use, in place of its own
Guid, so that these compare without a conversion through text.

GPT, like other EFI GUIDs, stores the first three groups of a GUID
little-endian, which Uuid::from_bytes_le and Uuid::to_bytes_le convert.

Part of #621.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1b4b569c-a481-43aa-bfd8-c14a9762c371
Say in the CRC module's header that a CRC-32 is 32 bits, so that the
CRC, its polynomial, and each entry of its table are u32s, and that
the table holds the CRC of each of the 256 values of a byte.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1b4b569c-a481-43aa-bfd8-c14a9762c371
Copilot AI balanced review requested due to automatic review settings October 22, 2026 04:31
@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/a3-gpt-validation-20261020 branch from 2a43977 to 7265b64 Compare October 22, 2026 04:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

Static review found no blocking issues in the bounded parser or its regression coverage; project tests were not rerun during this review.

No open issues 🎉

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) merged commit a6785cd into dev Oct 22, 2026
33 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the esaurez/a3-gpt-validation-20261020 branch October 22, 2026 15:25
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