Skip to content

Commit faff6da

Browse files
authored
falcon-lab: validate npuvm checksum (#820)
Before using the downloaded npuvm, we should validate its checksum. This ensures that in cases where the download fails or results in a corrupted file, we can fail early instead of waiting for later test logic to detect the issue (e.g. timeouts in wait_for_dpd()). Signed-off-by: Trey Aspelund <trey@oxidecomputer.com>
1 parent 65cc70a commit faff6da

1 file changed

Lines changed: 57 additions & 9 deletions

File tree

falcon-lab/src/dendrite.rs

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,63 @@ impl DendriteNode {
6868
const BUILDOMAT_URL: &str =
6969
"https://buildomat.eng.oxide.computer/public/file/oxidecomputer/";
7070
info!(d.log, "{}: setting up npuvm", self.name(&d));
71-
d.exec(
72-
self.0,
73-
&format!(
74-
"curl --retry 5 -OL \
75-
{BUILDOMAT_URL}/softnpu/image/{}/npuvm",
76-
commits.npuvm,
77-
),
78-
)
79-
.await?;
71+
// The fetch runs inside the guest over the serial console, which
72+
// surfaces neither curl's exit status nor a truncated transfer. Rather
73+
// than blindly exec a possibly-invalid binary, download it alongside
74+
// its published checksum and verify before continuing.
75+
const NPUVM_FETCH_ATTEMPTS: u32 = 5;
76+
let mut last_err = None;
77+
for attempt in 1..=NPUVM_FETCH_ATTEMPTS {
78+
let result: Result<()> = async {
79+
d.exec(
80+
self.0,
81+
&format!(
82+
"curl --fail --retry 5 --retry-all-errors \
83+
--remote-name-all -L \
84+
{BUILDOMAT_URL}/softnpu/image/{commit}/npuvm \
85+
{BUILDOMAT_URL}/softnpu/image/{commit}/npuvm.sha256.txt",
86+
commit = commits.npuvm,
87+
),
88+
)
89+
.await?;
90+
let expected = d.exec(self.0, "cat npuvm.sha256.txt").await?;
91+
let actual = d.exec(self.0, "digest -a sha256 npuvm").await?;
92+
let expected = expected.split_whitespace().next().unwrap_or_default();
93+
let actual = actual.split_whitespace().next().unwrap_or_default();
94+
if expected.is_empty() || expected != actual {
95+
return Err(anyhow!(
96+
"npuvm checksum mismatch: \
97+
expected {expected:?}, got {actual:?}"
98+
));
99+
}
100+
Ok(())
101+
}
102+
.await;
103+
match result {
104+
Ok(()) => {
105+
info!(
106+
d.log,
107+
"{}: npuvm downloaded and checksum verified \
108+
(attempt {attempt}/{NPUVM_FETCH_ATTEMPTS})",
109+
self.name(&d)
110+
);
111+
last_err = None;
112+
break;
113+
}
114+
Err(e) => {
115+
slog::warn!(
116+
d.log,
117+
"{}: npuvm fetch/verify attempt \
118+
{attempt}/{NPUVM_FETCH_ATTEMPTS} failed: {e:#}",
119+
self.name(&d)
120+
);
121+
last_err = Some(e);
122+
}
123+
}
124+
}
125+
if let Some(e) = last_err {
126+
return Err(e.context("failed to fetch and verify npuvm"));
127+
}
80128
d.exec(self.0, "chmod +x npuvm").await?;
81129
// Capture install stdout to land in the buildomat log. Without this
82130
// they're only visible inside the softnpu VM and lost on teardown.

0 commit comments

Comments
 (0)