fix: make the public headers self-contained, define two outputs, pin the third-party actions - #146
Merged
Conversation
common_bip39.h, common_sskr.h and common.h use uint8_t, size_t and bool
without including <stdint.h>, <stddef.h> or <stdbool.h>; sss/sss.h uses size_t
without <stddef.h>. They compile anywhere their callers pull those in first,
and nowhere else. It showed twice: the bool added to sskr.h and sss/sss.h for
the randomness callback did not compile, and neither did a file whose first
include was common_bip39.h.
Nothing already in the suite could hold this. Every file that includes these
headers also includes os.h, cx.h or the standard headers ahead of them, so the
property stays green while unmet.
tests/unit/tests/header_self_contained.c is one translation unit per header,
the same source compiled once for each with a different HEADER_UNDER_TEST.
Compiling is the whole test; the executable only depends on the objects.
Two earlier shapes of that check did not work, which is why it looks like
this:
- one file including every header in turn passes with the dependency unmet,
because the second header is satisfied by whatever the first one included.
Stripping the includes back out of common_sskr.h left it compiling;
- putting cx_errors.h in front of all of them has the same effect from a
different direction, since it includes <stdint.h> itself. With that in
place, stripping the includes out of any header still compiled.
So cx_errors.h now precedes only common.h, which needs cx_err_t for
compare_recovery_phrase_finish(). That makes common.h the one header this
cannot falsify: cx_errors.h brings <stdint.h> and, through ledger_assert.h,
<stdbool.h>, so deleting its own includes still compiles. They are kept
because they state what it uses, and the file says that no test holds them.
The other ten headers were each checked by deleting their includes and
watching the build fail.
bolos_ux_bip39_to_sskr_convert() writes *share_count only inside the branch that runs when the mnemonic decodes. A mnemonic the decoder refuses skips the whole body and returns 1, leaving the caller's count at whatever it held -- and both callers keep it in a struct that outlives the call, where it is the number of share screens the review then pages through. Not reachable today, because the phrase is checked before this is called, but it is the one output of this function that was undefined on a path out of it. Set at the top, like sskr_generate_shards() already sets *shard_len. sss_create_digest() copies four bytes of an HMAC into the caller's buffer and left the other twenty-eight on the stack. They are not the secret -- HMAC does not run backwards -- but every other local in that file is erased, including the digest and the coordinate arrays a few lines below, and this one had no reason to be the exception.
dawidd6/action-download-artifact and ncipollo/release-action were on major version tags, which are mutable, and both run inside the release job -- the one that holds contents: write, downloads a build artifact and publishes it. Moving either tag changes what runs with write access to this repository. actions/checkout is pinned alongside them for the same reason, at the two versions the workflows already used. Each SHA is the commit the tag pointed at when this was written, with the tag kept beside it as a comment so the next reader can tell which version it is and Dependabot can still offer an update. The reusable workflows under LedgerHQ/ledger-app-workflows stay on @v1. That is Ledger's own convention for their applications, and pinning them here would freeze this repository against fixes made on their side; it is an accepted risk rather than an oversight.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three small things, each one an output or a dependency that was left to whatever happened to be around it.
The public headers of
src/commondid not stand on their owncommon_bip39.h,common_sskr.handcommon.huseuint8_t,size_tandboolwithout including<stdint.h>,<stddef.h>or<stdbool.h>;sss/sss.husessize_twithout<stddef.h>. They compile anywhere their callers pull those in first, and nowhere else. It showed twice recently: thebooladded tosskr.handsss/sss.hfor the randomness callback did not compile, and neither did a file whose first include wascommon_bip39.h.Nothing already in the suite could hold this — every file that includes these headers also includes
os.h,cx.hor the standard headers ahead of them, so the property stays green while unmet.tests/unit/tests/header_self_contained.cis one translation unit per header: the same source compiled once for each with a differentHEADER_UNDER_TEST. Compiling is the whole test.Two earlier shapes of that check did not work, which is why it looks like this:
common_sskr.hleft it compiling;cx_errors.hin front of all of them has the same effect from another direction, since it includes<stdint.h>itself.So
cx_errors.hnow precedes onlycommon.h, which needscx_err_t. That makescommon.hthe one header this cannot falsify:cx_errors.hbrings<stdint.h>and, throughledger_assert.h,<stdbool.h>, so deleting its own includes still compiles. They are kept because they state what it uses, and the file says plainly that no test holds them. The other ten were each checked by deleting their includes and watching the build fail.Two outputs left undefined
bolos_ux_bip39_to_sskr_convert()writes*share_countonly inside the branch that runs when the mnemonic decodes. A mnemonic the decoder refuses skips the whole body and returns 1, leaving the caller's count at whatever it held — and both callers keep it in a struct that outlives the call, where it is the number of share screens the review then pages through. Not reachable today, since the phrase is checked before this is called, but it is the one output of this function that was undefined on a path out of it.sss_create_digest()copies four bytes of an HMAC into the caller's buffer and left the other twenty-eight on the stack. They are not the secret — HMAC does not run backwards — but every other local in that file is erased, including the digest and the coordinate arrays a few lines below.Third-party actions pinned
dawidd6/action-download-artifactandncipollo/release-actionwere on major version tags, which are mutable, and both run inside the release job — the one holdingcontents: writethat downloads a build artefact and publishes it.actions/checkoutis pinned alongside them, at the two versions the workflows already used. Each SHA carries its tag as a comment so the version stays readable and Dependabot can still offer updates.The reusable workflows under
LedgerHQ/ledger-app-workflowsstay on@v1: that is Ledger's convention for their applications, and pinning them here would freeze this repository against fixes made on their side.Verification
clang-formatclean oversrc/,actionlintreports nothing new