Igvmfilegen: Support patch-CoRIM command#3088
Igvmfilegen: Support patch-CoRIM command#3088mingweishih wants to merge 21 commits intomicrosoft:mainfrom
Conversation
Cherry-picked from igvm_sign_time_claim branch. Adds corim.rs module for reading/writing CoRIM document and signature headers in IGVM files, and patch-corim / dump-corim CLI subcommands. Uses igvm/igvm_defs from mingweishih/igvm corim-header branch. Signed-off-by: Ming-Wei Shih <mishih@microsoft.com>
Signed-off-by: Ming-Wei Shih <mishih@microsoft.com>
There was a problem hiding this comment.
Pull request overview
Adds CoRIM (Concise Reference Integrity Manifest) patching/extraction capabilities to igvmfilegen, enabling post-build injection of CoRIM document/signature data into an existing IGVM file (including support for splitting bundled COSE_Sign1 inputs).
Changes:
- Add new
DumpCorimandPatchCorimCLI subcommands to inspect and patch CoRIM headers in IGVM files. - Introduce a new
corimmodule implementing IGVM directive patching plus minimal COSE_Sign1 split/validation utilities (with unit tests). - Switch
igvm/igvm_defsworkspace deps to a git fork/branch that includes the required CoRIM header support.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| vm/loader/igvmfilegen/src/main.rs | Adds new CLI commands and implements CoRIM header dumping/patching entrypoints. |
| vm/loader/igvmfilegen/src/corim/mod.rs | Adds IGVM-level CoRIM patching logic and compatibility-mask helpers. |
| vm/loader/igvmfilegen/src/corim/cose.rs | Adds COSE_Sign1 parsing/splitting/validation helpers plus unit tests. |
| vm/loader/igvmfilegen/Cargo.toml | Adds open_enum dependency for the new COSE/CBOR helpers. |
| Cargo.toml | Switches igvm and igvm_defs from crates.io to a git fork/branch. |
| Cargo.lock | Updates lock entries for the new git-based igvm crates and incidental dependency resolution changes. |
| igvm = { git = "https://github.com/chris-oo/igvm", branch = "corim-header" } | ||
| igvm_defs = { git = "https://github.com/chris-oo/igvm", branch = "corim-header", default-features = false } |
There was a problem hiding this comment.
Switching igvm/igvm_defs from crates.io to a personal GitHub fork/branch has supply-chain and long-term maintenance implications. If this is intended as a temporary fork, add an explanatory comment (similar to nearby forked deps) and consider pinning to a specific rev (commit) in Cargo.toml to make the dependency source explicit and stable; alternatively, upstream the required changes and return to the published crate versions.
| igvm = { git = "https://github.com/chris-oo/igvm", branch = "corim-header" } | |
| igvm_defs = { git = "https://github.com/chris-oo/igvm", branch = "corim-header", default-features = false } | |
| # Temporary fork of igvm/igvm_defs for CORIM header support. | |
| # Pinned to a specific commit; upstream these changes and return to the | |
| # crates.io igvm/igvm_defs crates when possible. | |
| igvm = { git = "https://github.com/chris-oo/igvm", rev = "REPLACE_WITH_CORIM_HEADER_COMMIT_SHA" } | |
| igvm_defs = { git = "https://github.com/chris-oo/igvm", rev = "REPLACE_WITH_CORIM_HEADER_COMMIT_SHA", default-features = false } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| /// headers. This avoids using `IgvmFile::new_from_binary()` which | ||
| /// cannot parse CoRIM headers. |
There was a problem hiding this comment.
This test helper comment says IgvmFile::new_from_binary() cannot parse CoRIM headers, but the production patch_corim() implementation relies on parsing CorimDocument/CorimSignature via new_from_binary(). Please update the comment to reflect the current requirement/behavior (e.g., that you’re parsing raw headers to assert on binary layout rather than parser limitations).
| /// headers. This avoids using `IgvmFile::new_from_binary()` which | |
| /// cannot parse CoRIM headers. | |
| /// headers. This intentionally parses the raw headers instead of using | |
| /// `IgvmFile::new_from_binary()` so tests can assert on the exact binary | |
| /// layout of the CoRIM-related variable headers and payload ranges. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
vm/loader/igvmfilegen/src/main.rs
Outdated
| let aligned_len = (hdr.length as usize + 7) & !7; | ||
| plat_offset = data_offset + aligned_len; |
There was a problem hiding this comment.
In the platform-header scan, aligned_len = (hdr.length as usize + 7) & !7 and plat_offset = data_offset + aligned_len are unchecked. A malformed hdr.length can overflow or fail to advance plat_offset, leading to an infinite loop or out-of-bounds reads. Mirror the overflow/next_offset validation you do in the main variable-header loop (checked_add + ensure next_offset > current).
| let aligned_len = (hdr.length as usize + 7) & !7; | |
| plat_offset = data_offset + aligned_len; | |
| let raw_len = usize::try_from(hdr.length).map_err(|_| { | |
| anyhow::anyhow!( | |
| "Variable header at offset {} has length {} which does not fit in usize", | |
| plat_offset, | |
| hdr.length | |
| ) | |
| })?; | |
| let aligned_len = raw_len | |
| .checked_add(7) | |
| .map(|v| v & !7) | |
| .ok_or_else(|| { | |
| anyhow::anyhow!( | |
| "Variable header at offset {} has length {} which overflows when aligned", | |
| plat_offset, | |
| hdr.length | |
| ) | |
| })?; | |
| let next_offset = data_offset.checked_add(aligned_len).ok_or_else(|| { | |
| anyhow::anyhow!( | |
| "Variable header at offset {} has length {} which overflows when computing next offset", | |
| plat_offset, | |
| hdr.length | |
| ) | |
| })?; | |
| if next_offset <= plat_offset { | |
| bail!( | |
| "Variable header at offset {} has non-advancing length {} (aligned to {})", | |
| plat_offset, | |
| hdr.length, | |
| aligned_len | |
| ); | |
| } | |
| if next_offset > var_headers.len() { | |
| bail!( | |
| "Truncated variable header at offset {}: length {} (aligned {}) extends past end of table (len {})", | |
| plat_offset, | |
| hdr.length, | |
| aligned_len, | |
| var_headers.len() | |
| ); | |
| } | |
| plat_offset = next_offset; |
| match major { | ||
| CborMajorType::UNSIGNED_INT | CborMajorType::NEGATIVE_INT => Ok(off), | ||
| CborMajorType::BYTE_STRING | CborMajorType::TEXT_STRING => { | ||
| let end = off + arg as usize; |
There was a problem hiding this comment.
cbor_skip_item computes end = off + arg as usize without overflow checking. With attacker-controlled CBOR lengths this can wrap and cause incorrect offsets or later panics. Use checked_add (and return an error on overflow) when advancing offsets based on decoded lengths.
| let end = off + arg as usize; | |
| let Some(end) = off.checked_add(arg as usize) else { | |
| anyhow::bail!( | |
| "CBOR string length {arg} at offset {offset} overflows usize when added to offset" | |
| ); | |
| }; |
| CborMajorType::ARRAY => { | ||
| for _ in 0..arg { | ||
| off = cbor_skip_item(data, off)?; | ||
| } | ||
| Ok(off) |
There was a problem hiding this comment.
cbor_skip_item is fully recursive for arrays/maps/tags. A deeply nested (but valid) CBOR input can cause stack overflow and abort the process, which is undesirable when handling untrusted inputs. Consider adding a maximum nesting depth parameter/limit or rewriting this as an iterative walk.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
vm/loader/igvmfilegen/src/main.rs
Outdated
| platform: Option<Platform>, | ||
| /// Output directory to extract CoRIM payload data. For each matching CoRIM header, | ||
| /// the payload is written to a file named `corim_document_<N>.cbor` or | ||
| /// `corim_signature_<N>.cose`, where `<N>` is a zero-based index for that type. |
There was a problem hiding this comment.
The DumpCorim help says output filenames use a zero-based <N>, but the implementation increments document_count/signature_count before using them, producing 1-based numbering (e.g. corim_document_1.cbor). Please either start counts at 0 when formatting filenames/output, or update the help text to match the 1-based behavior.
| /// `corim_signature_<N>.cose`, where `<N>` is a zero-based index for that type. | |
| /// `corim_signature_<N>.cose`, where `<N>` is a 1-based index for that type. |
vm/loader/igvmfilegen/src/main.rs
Outdated
| fs_err::write(&output, &patched_igvm) | ||
| .context(format!("writing output IGVM file at {}", output.display()))?; |
There was a problem hiding this comment.
PatchCorim allows output to equal input for in-place edits, but the implementation writes directly to the target path. A crash/interruption during the write can corrupt the IGVM file. Consider writing to a temporary file in the same directory and then atomically renaming it into place (and/or only allowing in-place updates with an explicit --in-place flag).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…vmfilegen_corim_write
| /// Dump CoRIM (Concise Reference Integrity Manifest) headers and payloads from an IGVM file. | ||
| /// | ||
| /// This command scans the IGVM variable headers for CoRIM-related entries and prints or | ||
| /// extracts their contents. By default, all supported CoRIM headers for all platforms found | ||
| /// in the file are dumped. Use `--header-type` and `--platform` to narrow the selection. | ||
| /// | ||
| /// When `--output` is not specified, a human-readable summary of the selected CoRIM headers | ||
| /// is written to stdout. When `--output <dir>` is provided, the CoRIM payloads are written | ||
| /// to files in the given directory and the file paths are reported. | ||
| DumpCorim { |
There was a problem hiding this comment.
This PR adds new igvmfilegen CLI surface area (dump-corim, patch-corim), but the OpenVMM Guide still has an igvmfilegen entry with an empty link and no dev-tools page under Guide/src/dev_guide/dev_tools/. Please add/update the Guide page and wire it into Guide/src/SUMMARY.md so the new commands are discoverable.
This PR adds the support of writing CoRIM to an existing IGVM file, allowing for injecting the CoRIM files post build in the pipeline.
By IGVM design, a signed CoRIM is expected to be broke into payload and signed envelope with payload detached and wrote into
CORIM_DOCUMENTandCORIM_SIGNATUREentries correspondingly. The idea is decoupling the payload generation and the signing process. However, given that the CoRIM detached signing might not always be supported by the signing infrastructure, the tool additionally supports taking a signed CoRIM as input and handles the decoupling before writing to the IGVM file.