Skip to content

Attempt a basic PC range check as part of hubris.validate#711

Open
labbott wants to merge 1 commit into
masterfrom
check_rot_a_b_image
Open

Attempt a basic PC range check as part of hubris.validate#711
labbott wants to merge 1 commit into
masterfrom
check_rot_a_b_image

Conversation

@labbott

@labbott labbott commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

validate on a hubris archive will verify the image_id present in an archive matches what's in flash. This has an unfortunate failure mode for the RoT where we partition the flash manually. It's possible to have the image ID check succeed on the archive but actually be running the opposite image. This causes all kinds of confusion. Add a basic spot check that the PC is in at least one module range. This could cause potential failures if the PC is actually running in a range that does not correspond to a module. Based on other work, the chances of this happening are very limited and should be transient.

`validate` on a hubris archive will verify the `image_id` present
in an archive matches what's in flash. This has an unfortunate
failure mode for the RoT where we partition the flash manually.
It's possible to have the image ID check succeed on the archive
but actually be running the opposite image. This causes all kinds
of confusion. Add a basic spot check that the PC is in at least
one module range. This could cause potential failures if the PC
is actually running in a range that does not correspond to a
module. Based on other work, the chances of this happening are
very limited and should be transient.
@labbott

labbott commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author
labbott@labbott-desktop2:~/humility$ ./target/debug/humility -a ~/hubris/target/oxide-rot-1-selfsigned/dist/a/build-oxide-rot-1-selfsigned-image-a.zip tasks
humility: attached via 1fc9:143:Some("U2VLC0GBANWF1") MCU-LINK (r0FB) CMSIS-DAP V3.172
system time = 200385
ID TASK                       GEN PRI STATE    
 0 jefe                         0   0 recv, notif: fault timer(T+15)
 1 hiffy                        0   6 notif: timer(T+18)
 2 idle                         0   7 RUNNING
 3 update_server                0   3 recv
 4 syscon_driver                0   2 recv
 5 gpio_driver                  0   3 recv
 6 sprot                        0   6 notif: spi-irq(irq59)
 7 swd                          0   4 recv, notif: timer sp_reset-irq(irq4) jtag_detect-irq(irq5)
 8 dumper                       0   5 recv
 9 attest                       0   3 recv
labbott@labbott-desktop2:~/humility$ ./target/debug/humility -a ~/hubris/target/oxide-rot-1-selfsigned/dist/b/build-oxide-rot-1-selfsigned-image-b.zip tasks
humility: attached via 1fc9:143:Some("U2VLC0GBANWF1") MCU-LINK (r0FB) CMSIS-DAP V3.172
humility tasks failed: PC at 0x1ef34 is not part of any module. This is likely an incorrect archive.

Comment on lines +2110 to +2115
if let Ok(pc) = core.read_reg(ARMRegister::PC) {
if self.instr_mod(pc).is_none() {
bail!("PC at 0x{pc:x} is not part of any module. This is \
likely an incorrect archive.");
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i believe clippy would like you to have written something more like:

Suggested change
if let Ok(pc) = core.read_reg(ARMRegister::PC) {
if self.instr_mod(pc).is_none() {
bail!("PC at 0x{pc:x} is not part of any module. This is \
likely an incorrect archive.");
}
}
if let Ok(pc) = core.read_reg(ARMRegister::PC)
&& self.instr_mod(pc).is_none() {
bail!("PC at 0x{pc:x} is not part of any module. \
This is likely an incorrect archive.");
}

core.halt()?;
if let Ok(pc) = core.read_reg(ARMRegister::PC) {
if self.instr_mod(pc).is_none() {
bail!("PC at 0x{pc:x} is not part of any module. This is \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

unimportant turbo nitpick: if we write

Suggested change
bail!("PC at 0x{pc:x} is not part of any module. This is \
bail!("PC at {pc:#x} is not part of any module. This is \

that tells the formatter to emit the 0x prefix itself. which...changes absolutely nothing here so it doesn't matter...

core.halt()?;
if let Ok(pc) = core.read_reg(ARMRegister::PC) {
if self.instr_mod(pc).is_none() {
bail!("PC at 0x{pc:x} is not part of any module. This is \

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 way this interacts with flashing is not ideal. If you try to re-flash an A image that's already been flashed to the A slot while the RoT is running a B image:

  • the old behavior was to fail with "archive appears to be already flashed on attached device; use -F ("--force") to force re-flash"
  • this PR's behavior is to re-flash the A image
  • the ideal behavior is probably to warn the user that they probably meant to flash the other image, then fail with the "archive appears to be already flashed" error.

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.

yeah you're right that's confusing behavior. I think that would point to needing to fix validate to return a more specific error

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.

Or don't use validate for the pre-flash check. In my PR, I considered a bunch of different return types then gave up trying to make the same validate function work for flashing and for both of the HubrisValidate options.

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.

3 participants