Skip to content

Pin the .efw wire format in eBoot — a field swap and every constant value drift undetected #66

Closed

Description

@srpatcha

The .efw image header is a wire format with two independent definitions:

repo file type pins
eFirmware include/efw/efw_image.h efw_image_header_t tests/test_abi.c, 75 checks (eFirmware#2)
eBoot include/eos_image.h eos_image_header_t 4 _Static_asserts

eBoot parses the images eFirmware produces, so the layout is shared. Both sides
pin something. Neither pins enough, and nobody compares the two sets of numbers.

1. Four of fourteen fields are pinned — a field swap passes silently

eBoot asserts sizeof == 156 and the offsets of hash, sig_type, signature.
Ten fields have no assert. Swapping two adjacent same-width fields moves neither
the size nor any pinned offset. I compiled the header with load_addr and
entry_addr transposed — a plausible editing slip:

all four existing static asserts: PASSED
load_addr  is now at offset 16 (was 12)
entry_addr is now at offset 12 (was 16)

Clean compile. The bootloader then loads the image at its entry point and jumps
to its load address.

2. No constant's value is pinned on this side

EOS_IMG_MAGIC, EOS_HASH_SIZE, EOS_SIG_MAX_SIZE, EOS_IMG_SIGNED_LEN and
the five eos_sig_type_t values all travel inside the image, and all are wire
format. Changing EOS_SIG_ED25519 = 3 to 4, or reordering the enum, compiles
cleanly, passes all four static asserts, and makes eBoot misread the signature
type of every image already in the field. eFirmware pins EFW_IMAGE_MAGIC == 0x454F5349u and EFW_SIG_ED25519 == 3 on its side; eBoot pins neither.

3. The two repos disagree on header version — found while measuring the above

value
EFW_IMAGE_HDR_VERSION (eFirmware, stamped into every image by efw_image.c:55) 1
EOS_IMAGE_HDR_VERSION (eBoot) 2

eBoot's own header documents what the difference means:

v1 — the digital signature covered hash[] only.
v2 — the signature covers the whole header prefix.
v1 images must be re-signed; their signatures do not verify under v2.

core/image_verify.c:89 accepts any version in 1..2. core/image_verify.c:202
then verifies over EOS_IMG_SIGNED_LEN unconditionally — there is no branch
on hdr_version anywhere in the file. So a v1 image is admitted by the parser and
then fails signature verification, by eBoot's own stated definition of v1.

This fails closed — it rejects, it does not accept a forgery — so it is not an
escalation. But every signed image the organisation's own tool produces is stamped
v1, and eBoot supports only v2. Either the parser should reject v1 outright with
EOS_ERR_VERSION, or the verifier should branch, or efwtool should stamp 2. That
is a security-policy call, so it is not part of the PR below — it needs a
decision.

What the PR does

Adds tests/unit/test_image_abi.c: every field offset and width, sizeof, and
the value of every shared constant including the signature-type enum — the same
treatment eFirmware#2 gives its side, plus the constant values eFirmware's test
covers and eBoot's asserts do not.

Scope is deliberately the drift detector only. Items 1 and 2 are closed by it.
Item 3 is reported, not fixed.

Blocked on

master does not build (eos_crc32 conflicting types); #58 repairs it. This
branches from #58.

Activity

  1. changed the title [-]Pin the .efw wire format in eBoot — two independent definitions with no cross-check[/-] [+]Pin the .efw wire format in eBoot — a field swap and every constant value drift undetected[/+] on Aug 30, 2026
  2. added a commit that references this issue on Sep 3, 2026
    bbd997a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions