Skip to content

feat: require SEV launch measurements for new GuestOS versions - #11052

Open
r-birkner wants to merge 1 commit into
masterfrom
rjb/require-launch-measurement
Open

feat: require SEV launch measurements for new GuestOS versions#11052
r-birkner wants to merge 1 commit into
masterfrom
rjb/require-launch-measurement

Conversation

@r-birkner

@r-birkner r-birkner commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Electing a GuestOS version now requires SEV-SNP launch measurements. Without a
measurement, nodes running the version cannot be attested, so a version that
lacks one should never become electable in the first place.

Note for release tooling: a ReviseElectedGuestosVersions proposal submitted
without --guest-launch-measurements-path will now be rejected.

@github-actions github-actions Bot added the chore label Aug 6, 2026
@r-birkner r-birkner changed the title chore: require SEV launch measurements for new GuestOS versions feat: require SEV launch measurements for new GuestOS versions Aug 6, 2026
@github-actions github-actions Bot added feat and removed chore labels Aug 6, 2026
@r-birkner
r-birkner marked this pull request as ready for review August 6, 2026 14:47
@r-birkner
r-birkner requested review from a team as code owners August 6, 2026 14:47

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This pull request changes code owned by the Governance team. Therefore, make sure that
you have considered the following (for Governance-owned code):

  1. Update unreleased_changelog.md (if there are behavior changes, even if they are
    non-breaking).

  2. Are there BREAKING changes?

  3. Is a data migration needed?

  4. Security review?

How to Satisfy This Automatic Review

  1. Go to the bottom of the pull request page.

  2. Look for where it says this bot is requesting changes.

  3. Click the three dots to the right.

  4. Select "Dismiss review".

  5. In the text entry box, respond to each of the numbered items in the previous
    section, declare one of the following:

  • Done.

  • $REASON_WHY_NO_NEED. E.g. for unreleased_changelog.md, "No
    canister behavior changes.", or for item 2, "Existing APIs
    behave as before.".

Brief Guide to "Externally Visible" Changes

"Externally visible behavior change" is very often due to some NEW canister API.

Changes to EXISTING APIs are more likely to be "breaking".

If these changes are breaking, make sure that clients know how to migrate, how to
maintain their continuity of operations.

If your changes are behind a feature flag, then, do NOT add entrie(s) to
unreleased_changelog.md in this PR! But rather, add entrie(s) later, in the PR
that enables these changes in production.

Reference(s)

For a more comprehensive checklist, see here.

GOVERNANCE_CHECKLIST_REMINDER_DEDUP

@zeropath-ai

zeropath-ai Bot commented Aug 6, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to f5ba971.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► rs/nns/integration_tests/src/upgrades_handler.rs
    Add guest launch measurements support for test upgrades
► rs/registry/canister/src/mutations/do_revise_elected_replica_versions.rs
    Enhance ReviseElectedGuestosVersionsPayload with guest_launch_measurements validation and error messaging
► rs/registry/canister/src/mutations/do_revise_elected_replica_versions/tests.rs
    Add tests for new guest_launch_measurements validation behavior (in new file)
Enhancement ► rs/registry/canister/unreleased_changelog.md
    Document updated behavior for guest_launch_measurements in revise_elected_guestos_versions
Enhancement ► rs/tests/networking/nns_delegation_test.rs
    Update test to include guestos update launch measurements in upgrade flow

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR tightens ReviseElectedGuestosVersions validation so that electing a new GuestOS/replica version requires SEV-SNP launch measurements, preventing versions that cannot be attested from becoming electable.

Changes:

  • Treat guest_launch_measurements as a required “elect version” parameter (must be set/unset together with the other election parameters) and improve validation errors by listing missing parameters.
  • Validate provided launch measurements in ReviseElectedGuestosVersionsPayload::validate to catch malformed data before proposal submission/execution.
  • Update system/integration tests and changelog to reflect the new requirement.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
rs/tests/networking/nns_delegation_test.rs Passes launch measurements when electing an upgrade version in the networking test flow.
rs/registry/canister/unreleased_changelog.md Documents the new validation requirement and updated rejection behavior/error messaging.
rs/registry/canister/src/mutations/do_revise_elected_replica_versions/tests.rs Adds unit tests covering valid/invalid/missing launch measurement scenarios for the payload.
rs/registry/canister/src/mutations/do_revise_elected_replica_versions.rs Enforces “all-or-nothing” election parameters including measurements and validates measurement contents.
rs/nns/integration_tests/src/upgrades_handler.rs Updates integration tests to include measurements on election and adds negative cases for missing/empty measurements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +54 to +64
fn guest_launch_measurements_for_test() -> GuestLaunchMeasurements {
GuestLaunchMeasurements {
guest_launch_measurements: vec![GuestLaunchMeasurement {
measurement: vec![0x42; 48],
metadata: Some(GuestLaunchMeasurementMetadata {
kernel_cmdline: Some("foo=bar".to_string()),
vcpu_type: None,
}),
}],
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you convert this (0-argument new function) into a lazy_static!?

Comment on lines +54 to +64
fn guest_launch_measurements_for_test() -> GuestLaunchMeasurements {
GuestLaunchMeasurements {
guest_launch_measurements: vec![GuestLaunchMeasurement {
measurement: vec![0x42; 48],
metadata: Some(GuestLaunchMeasurementMetadata {
kernel_cmdline: Some("foo=bar".to_string()),
vcpu_type: None,
}),
}],
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there some constant somewhere for the length of this?

.unwrap_or_default(),
guest_launch_measurements: elect
.as_ref()
.map(|_| guest_launch_measurements_for_test()),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

map that doesn't actually use the argument is kind of sus. I mean, I have no better idea. This is just highly highly unusual. Yes, existing code already does this. That code is also highly sus.

guest_launch_measurements: elect
.as_ref()
.map(|_| guest_launch_measurements_for_test()),
replica_version_to_elect: elect,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would have put this first, since this is the thing that's really important. Lead with the lede.

Ofc, the code already put this in a weird position -> do not change in this PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the usual pattern here is to inline tests.

I guess Claude noticed this, but Consistency is king. Or did you choose to do this in a separate file?

Don't get me wrong: I love separate file (because it maximizes build caching), but consistency is king. My plan is to do a sweep, and move tests to separate files. But for now, let's be consistent.

let payload = ReviseElectedGuestosVersionsPayload {
replica_version_to_elect: Some(REPLICA_VERSION_ID.to_string()),
guest_launch_measurements: Some(guest_launch_measurements()),
..Default::default()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a very short test, so this is not so important, but try to point out the defect in the input. Here, it's hard to see, there is nothing you can directly point at, because the missing fields are taken care of by this line. Still, you could do something like

Suggested change
..Default::default()
// release_package_* fields are missing.
..Default::default()

@@ -104,31 +105,68 @@ pub struct ReviseElectedGuestosVersionsPayload {
impl ReviseElectedGuestosVersionsPayload {
pub fn is_electing_a_version(&self) -> Result<bool, String> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I know the code was already like this (so, do not fix it), but it is totally wacky for is_whatever to return Result instead of bool. Yes, I understand, it's a Result<bool, ...>, but that is not what people expect from is_whatever, at least I certainly do not.

Comment on lines +134 to +140
// Leave breadcrumbs: which parameters were missing.
let mut unset_params = Vec::new();
for (name, is_set) in elect_params {
if !is_set {
unset_params.push(name);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You are a good person. Rare.

Ok(())
} else {
Err("At least one version has to be elected or unelected.".into())
if !self.is_electing_a_version()? && !self.is_unelecting_a_version() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if !self.is_electing_a_version()? && !self.is_unelecting_a_version() {
let is_making_a_change = self.is_electing_a_version()? || self.is_unelecting_a_version()
if !is_making_a_change {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems verbose. I think all you need to say is

Guest launch measurements is now required (when electing a new Guestos version).

I think this more strongly leads with the led/avoids burying the lede.

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.

4 participants