Skip to content

fix(secure-boot): a debug lock that failed must not report a successful boot - #82

Open
Kartikey1306 wants to merge 1 commit into
embeddedos-org:masterfrom
Kartikey1306:fix/secure-boot-debug-lock-fail-open
Open

fix(secure-boot): a debug lock that failed must not report a successful boot#82
Kartikey1306 wants to merge 1 commit into
embeddedos-org:masterfrom
Kartikey1306:fix/secure-boot-debug-lock-fail-open

Conversation

@Kartikey1306

Copy link
Copy Markdown
Contributor

cfg.lock_debug asks for SWD/JTAG to be closed before the verified image runs. Step 7 of eos_secure_boot() honoured that request like this:

if (cfg->lock_debug) {
    eos_secure_boot_lock_debug();
}

/* ---- Step 8: Record successful attestation ---- */
attest_record(2, hdr.image_version, hdr.hash, NULL, EOS_SBOOT_OK);
return EOS_SBOOT_OK;

eos_secure_boot_lock_debug() returned void and discarded the result of the OTP write that actually blows the fuse. When that write failed, boot continued, attestation recorded EOS_SBOOT_OK, and the device ran the image with its debug port open — the exact condition the policy exists to prevent, reported as a clean secure boot.

The interesting case is not a flaky fuse

eos_hal_otp_write() returns EOS_ERR_NOT_SUPPORTED when the board provides no otp_write hook at all. So on every such board, lock_debug: true was silently a no-op — that is the default configuration, not an edge case.

The change

eos_secure_boot_lock_debug() returns int, and a caller that asked for the lock and did not get it now fails with EOS_SBOOT_ERR_POLICY — a code that already existed for exactly this ("Boot policy violation") — with the failure recorded in the attestation log instead of a success.

Why this was never observable

core/secure_boot.c is not in CMakeLists.txt. The module has never been compiled, so this path could not run and could not be tested:

$ grep -c "core/secure_boot.c" CMakeLists.txt
0

Added the one line that builds it — the same line #72 adds, written identically, so whichever lands first leaves the other a trivial rebase.

Tests

tests/unit/test_secure_boot_policy.c covers the three outcomes: fuse written, write failing, and a board with no otp_write. Kept in its own file so it does not collide with the test_secure_boot.c that #72 introduces.

Against master the new test does not compile:

error: invalid operands to binary expression ('void' and 'int')

because there is no result to check. That is the defect stated as a compile error rather than a runtime one.

Verification

build clean, 0 errors
ctest --no-tests=error 20/20 pass
pytest tests/ 30 passed

Stacked on #77 — master's test suite does not compile without it (test_image_verify.c lost a line continuation in #70).

Relationship to #72

Complementary, not competing. #72 builds the module and stops it booting plaintext; this fixes a separate fail-open inside the same function's policy handling. The only textual overlap is the one-line CMakeLists.txt addition, deliberately identical.

🤖 Generated with Claude Code

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…ul boot

cfg.lock_debug asks for SWD/JTAG to be closed before the verified image runs.
Step 7 of eos_secure_boot() honoured that request like this:

    if (cfg->lock_debug) {
        eos_secure_boot_lock_debug();
    }

    /* ---- Step 8: Record successful attestation ---- */
    attest_record(2, hdr.image_version, hdr.hash, NULL, EOS_SBOOT_OK);
    return EOS_SBOOT_OK;

eos_secure_boot_lock_debug() returned void and discarded the result of the OTP
write that actually blows the fuse. So when the write failed, boot continued,
attestation recorded EOS_SBOOT_OK, and the device ran the image with its debug
port open -- the exact condition the policy existed to prevent, reported as a
clean secure boot.

The interesting case is not a flaky fuse. eos_hal_otp_write() returns
EOS_ERR_NOT_SUPPORTED when the board provides no otp_write hook at all, so on
every such board `lock_debug: true` was silently a no-op. That is the default
configuration, not an edge case.

eos_secure_boot_lock_debug() now returns int, and a caller that asked for the
lock and did not get it fails with EOS_SBOOT_ERR_POLICY -- a code that already
existed for exactly this ("Boot policy violation") -- with the failure recorded
in the attestation log rather than a success.

Why this was never observable: core/secure_boot.c is not in CMakeLists.txt.
The module has never been compiled, so this path could not run and could not be
tested. Added the one line that builds it -- the same line embeddedos-org#72 adds, written
identically so whichever lands first leaves the other a trivial rebase.

tests/unit/test_secure_boot_policy.c covers the three outcomes: the fuse
written, the write failing, and a board with no otp_write. Kept in its own file
so it does not collide with the test_secure_boot.c embeddedos-org#72 introduces.

Against master the new test does not compile -- `invalid operands to binary
expression ('void' and 'int')` -- because there is no result to check. That is
the defect stated as a compile error.

Verified on this branch: build clean, ctest 20/20, pytest 30 passed.

Stacked on embeddedos-org#77 (master's test suite does not compile without it).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Kartikey1306
Kartikey1306 force-pushed the fix/secure-boot-debug-lock-fail-open branch from 998035f to c5bb181 Compare September 1, 2026 15:47
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.

2 participants