Skip to content

fix(assembly): validate procedure roots during library deserialization#2933

Merged
huitseeker merged 2 commits into0xMiden:nextfrom
giwaov:fix/library-deser-validate-roots
Apr 24, 2026
Merged

fix(assembly): validate procedure roots during library deserialization#2933
huitseeker merged 2 commits into0xMiden:nextfrom
giwaov:fix/library-deser-validate-roots

Conversation

@giwaov
Copy link
Copy Markdown
Contributor

@giwaov giwaov commented Mar 28, 2026

Description

This PR fixes an issue where \Library::read_from\ accepts procedure exports whose \MastNodeId\ is not a procedure root in the underlying MAST forest. This allows crafting a library with uncallable procedure exports, violating the invariant enforced by \Library::new().

This was identified as finding 19 in the audit report.

Changes

Fix

  • Added a validation loop to the \Deserializable\ implementation for \Library\ (in \crates/assembly-syntax/src/library/mod.rs) that checks each procedure export references a valid procedure root in the \MastForest, matching the existing check in \Library::new().

Test

  • Added \library_deserialization_rejects_non_root_export\ test that constructs a tampered serialized library with an export pointing to a non-root node and verifies deserialization rejects it with an appropriate error message.

Testing

  • \cargo test -p miden-assembly library_deserialization_rejects_non_root_export\ passes
  • \cargo test -p miden-assembly library_serialization\ still passes (no regression)
  • Both \miden-assembly\ and \miden-assembly-syntax\ crates compile cleanly

Closes #2831

@huitseeker
Copy link
Copy Markdown
Collaborator

huitseeker commented Mar 30, 2026

@amathxbt Please cease spam comments.

@giwaov giwaov force-pushed the fix/library-deser-validate-roots branch 2 times, most recently from 619f63f to 8d82445 Compare March 30, 2026 09:50
Copy link
Copy Markdown
Collaborator

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

🙏

@huitseeker huitseeker requested review from bitwalker and plafer March 31, 2026 11:27
@0xMiden 0xMiden deleted a comment from github-actions Bot Apr 7, 2026
@huitseeker huitseeker requested a review from bobbinth April 7, 2026 13:47
@bitwalker
Copy link
Copy Markdown
Collaborator

@giwaov Can you rebase this and resolve the CHANGELOG conflict? I think we can get this merged once that is done

Library::read_from accepts procedure exports whose MastNodeId is not a
procedure root in the underlying MAST forest. This means a malicious
library can export uncallable procedures, violating the invariant
enforced by Library::new().

Add a validation loop to the Deserializable implementation for Library
that checks each procedure export has a corresponding procedure root in
the MastForest, matching the existing check in Library::new().

Add a regression test that constructs a tampered library with an export
pointing to a non-root node and verifies deserialization rejects it.

Closes 0xMiden#2831
@giwaov giwaov force-pushed the fix/library-deser-validate-roots branch from 8d82445 to 1e97ee9 Compare April 23, 2026 23:22
@github-actions
Copy link
Copy Markdown

Automated check (CONTRIBUTING.md)

Findings:

  • Add a short Rationale explaining why the change is needed.

Recommendations:

  • Consider adding a Test plan or clear review steps.

Next steps:

@giwaov
Copy link
Copy Markdown
Contributor Author

giwaov commented Apr 23, 2026

Rebased onto the latest next and force-pushed the branch.

I also reran the regression test locally:

  • cargo test -p miden-assembly library_deserialization_rejects_non_root_export

That passes now, so this should be ready for another look.

exports.insert(path, export);
}

for export in exports.values() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I wonder if this should call Library::new(mast_forest, exports) instead of re-checking roots here. The last bug came from deserialization drifting away from the constructor path, and keeping the invariant and digest logic in one place would make the next validation change harder to miss.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated on the branch now. Library::read_from delegates to Self::new(mast_forest, exports) so the procedure-root invariant and digest computation stay on the constructor path instead of drifting here again.

I also re-ran cargo test -p miden-assembly library_deserialization_rejects_non_root_export locally.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Lol. Good bot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Haha, fair enough 😄 Thanks again for the review and for merging this one.

That makes 10 merged PRs on my side now, which I’m really happy about and would love to do more, I also wanted to ask: when Miden eventually goes mainnet, is there likely to be any kind of recognition or reward for contributors, or is it still too early to say? Thank you sir.

@huitseeker huitseeker merged commit 2d8746f into 0xMiden:next Apr 24, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Library deserialization accepts uncallable procedure exports

3 participants