Skip to content

Commit dd551fa

Browse files
committed
Include additional hashes in src/stage0
This patch changes `bump-stage0` to include: * The sha256 hash of the channel manifest used to create `src/stage0`. * The rust and rustfmt git commit in `src/stage0`. * Hashes of all the artifacts, like the source tarball, in `src/stage0`. Combined this will allow for: * Projects that bootstrap their own compiler, such as Fuchsia, or users of [bootstrap], to build their compilers offline without needing to communicate with static.rust-lang.org. * Auditors to detect if the channel manifest, and all the artifacts inside the manifest, were modified after it was used to generate `src/stage0`. Furthermore, if they did find modified artifacts, they could determine if the Rust Signing Key was compromised by checking if any modified file was signed properly. finally, it allows regeneration of `src/stage0` when specifying both the day of the build for rust, and the day of the build for rustfmt, which can allow a maintainer to regenerate `src/stage0` to verify nothing changed. [bootstrap]: https://github.com/dtolnay/bootstrap [mrustc]: https://github.com/thepowersgang/mrustc
1 parent 9f0e5d9 commit dd551fa

File tree

6 files changed

+126
-10
lines changed

6 files changed

+126
-10
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,10 @@ dependencies = [
334334
"anyhow",
335335
"build_helper",
336336
"curl",
337+
"hex",
337338
"indexmap",
338339
"serde",
340+
"sha2",
339341
"toml 0.5.11",
340342
]
341343

src/bootstrap/src/core/download.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ impl Config {
459459
return Some(PathBuf::new());
460460
}
461461

462-
let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?;
462+
let VersionMetadata { date, version, .. } = self.stage0_metadata.rustfmt.as_ref()?;
463463
let channel = format!("{version}-{date}");
464464

465465
let host = self.build;

src/build_helper/src/stage0_parser.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub struct Stage0 {
1010

1111
#[derive(Default, Clone)]
1212
pub struct VersionMetadata {
13+
pub channel_manifest_hash: String,
14+
pub git_commit_hash: String,
1315
pub date: String,
1416
pub version: String,
1517
}
@@ -50,9 +52,21 @@ pub fn parse_stage0_file() -> Stage0 {
5052
"git_merge_commit_email" => stage0.config.git_merge_commit_email = value.to_owned(),
5153
"nightly_branch" => stage0.config.nightly_branch = value.to_owned(),
5254

55+
"compiler_channel_manifest_hash" => {
56+
stage0.compiler.channel_manifest_hash = value.to_owned()
57+
}
58+
"compiler_git_commit_hash" => stage0.compiler.git_commit_hash = value.to_owned(),
5359
"compiler_date" => stage0.compiler.date = value.to_owned(),
5460
"compiler_version" => stage0.compiler.version = value.to_owned(),
5561

62+
"rustfmt_channel_manifest_hash" => {
63+
stage0.rustfmt.get_or_insert(VersionMetadata::default()).channel_manifest_hash =
64+
value.to_owned();
65+
}
66+
"rustfmt_git_commit_hash" => {
67+
stage0.rustfmt.get_or_insert(VersionMetadata::default()).git_commit_hash =
68+
value.to_owned();
69+
}
5670
"rustfmt_date" => {
5771
stage0.rustfmt.get_or_insert(VersionMetadata::default()).date = value.to_owned();
5872
}

src/stage0

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ nightly_branch=master
1313
# All changes below this comment will be overridden the next time the
1414
# tool is executed.
1515

16+
compiler_channel_manifest_hash=13b65dfadf9dd9bb722841e0ade5b6f6ebf59186f14af2fce8eb4e732e72032b
17+
compiler_git_commit_hash=95597e848d27a82b8864c677ab807e9f0be1f68c
1618
compiler_date=2025-05-26
1719
compiler_version=beta
20+
rustfmt_channel_manifest_hash=a70dbc2f0d4cddc940b4c8e413fc2bcb45a8a57d2abf3c5e29a3d4168ba90b4d
21+
rustfmt_git_commit_hash=2805e1dc4c18ed4c84d161502c48da870c56f68a
1822
rustfmt_date=2025-05-27
1923
rustfmt_version=nightly
2024

@@ -372,6 +376,15 @@ dist/2025-05-26/clippy-beta-x86_64-unknown-linux-musl.tar.gz=7af7df177e64881dd68
372376
dist/2025-05-26/clippy-beta-x86_64-unknown-linux-musl.tar.xz=f2f9575cbd3e3f067aeda5f9b7ab424e0dc119448d12872692cb7c6669f61ae0
373377
dist/2025-05-26/clippy-beta-x86_64-unknown-netbsd.tar.gz=5e1dc30da47902c52ab1fbfa2216a6952385184b44854c47b8eb988bdd1b040d
374378
dist/2025-05-26/clippy-beta-x86_64-unknown-netbsd.tar.xz=23f21905caa5824a463fac01e18e0055009cecdfd406da76b838105eb78127e7
379+
dist/2025-05-26/rust-beta-aarch64-pc-windows-msvc.msi=267cfb9bd514e402e9239add481ed0a910b109acbff3ef9c2d772ef1a682b32a
380+
dist/2025-05-26/rust-beta-i686-pc-windows-gnu.msi=44911ff27b8b717ca81cd0b52215600a10aadf0d152088e5765d7965d45563fd
381+
dist/2025-05-26/rust-beta-i686-pc-windows-msvc.msi=7171772f73325ef8f4b4adac13b0eb30633c7968b01267defb95e4e6f4c567bf
382+
dist/2025-05-26/rust-beta-x86_64-pc-windows-gnu.msi=79b3db15b584284e52879615bd8bf754ac8c7864c909b738cfa7d6a549ed3e76
383+
dist/2025-05-26/rust-beta-x86_64-pc-windows-msvc.msi=755da7c34499c5c3410a3314918b21d49f5b6c4e12b48879f5930b5ba17e0d35
384+
dist/2025-05-26/rust-beta-aarch64-apple-darwin.pkg=20862aad289534f608e52ef7a813e9bc4cae8d91829e31e81b85ef77fba6f90b
385+
dist/2025-05-26/rust-beta-x86_64-apple-darwin.pkg=f36add16fc74543210eb366f4bac8ac1ff6ab55e9d5beaf83802cfdb8d55f7a8
386+
dist/2025-05-26/rustc-beta-src.tar.gz=9e42c1a344896e4b8ed377e45a6a6495ec412b3f5fbe06000e504bc607d05bed
387+
dist/2025-05-26/rustc-beta-src.tar.xz=45bce011f4342e9814eeb0647e2f1127e5919a5eaa5329fcecc97765ff229628
375388
dist/2025-05-27/rustfmt-nightly-aarch64-apple-darwin.tar.gz=5a3b21df1d525a049b9bd1fca0e32eb5aad1a82a2800e094af80f121e90878c0
376389
dist/2025-05-27/rustfmt-nightly-aarch64-apple-darwin.tar.xz=3f7edd6997839f12d70246edb672a13d808bd871bfaa4bda66bb4db4fb1158fc
377390
dist/2025-05-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=99ecb24920480c06482d8b10f6dbc23247c66033991ad807f8228dff35626fac
@@ -476,3 +489,12 @@ dist/2025-05-27/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=d146af52aa7fad3b1
476489
dist/2025-05-27/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=d72ed1096917a5789f26564ddc920c3fdcd29056cf97452371e5141bcc2c8a8e
477490
dist/2025-05-27/rustc-nightly-x86_64-unknown-netbsd.tar.gz=94b608796d12feff92c54f942318e711879d86b1a3114a710b8366b7415ae025
478491
dist/2025-05-27/rustc-nightly-x86_64-unknown-netbsd.tar.xz=7d870360a35a34dffede096d62734d97a7bf60d0661e638f73d913cb93bd49ec
492+
dist/2025-05-27/rust-nightly-aarch64-pc-windows-msvc.msi=9737a96d76515d5b267226915c6917b4f5f2e5466ac5906660e8387e7cd5a453
493+
dist/2025-05-27/rust-nightly-i686-pc-windows-gnu.msi=d67eec08c0de0dc786577db9fdbf10106f4bfb4c016d2cba4798856c0e3073d9
494+
dist/2025-05-27/rust-nightly-i686-pc-windows-msvc.msi=4571c0eee00ac2cd6364b03c7b0053de6dc79be3fc8bd6db0c525282d31e46b6
495+
dist/2025-05-27/rust-nightly-x86_64-pc-windows-gnu.msi=704aab03718a9cbb6b061d8c7ce5a071cdc74bcf94eaf5c6d5350828efdd279e
496+
dist/2025-05-27/rust-nightly-x86_64-pc-windows-msvc.msi=e7ed14ac27c9ba94e1c07cbdd4348251d7af4c50d084dbb3d7ac3a5a952a7fbc
497+
dist/2025-05-27/rust-nightly-aarch64-apple-darwin.pkg=fdc071218f279d4a29b6865acb18a0122995a13ccc148a77ca525aad5852be12
498+
dist/2025-05-27/rust-nightly-x86_64-apple-darwin.pkg=ca1c62078b53ec361408dd32f5880d22d788cf83d735a69a743ffd7b8d394f63
499+
dist/2025-05-27/rustc-nightly-src.tar.gz=7ba390eb8110b21922d8dbbe80ee58e116a8eb290e0613db74bd666a47fed73b
500+
dist/2025-05-27/rustc-nightly-src.tar.xz=7341d94e81cb9300276cf1e9213b4d3f6a653fa8c26de1fb2354e3639943d39e

src/tools/bump-stage0/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ edition = "2021"
99
anyhow = "1.0.34"
1010
build_helper = { path = "../../build_helper" }
1111
curl = "0.4.38"
12+
hex = "0.4.2"
1213
indexmap = { version = "2.0.0", features = ["serde"] }
1314
serde = { version = "1.0.125", features = ["derive"] }
15+
sha2 = "0.10.1"
1416
toml = "0.5.7"

src/tools/bump-stage0/src/main.rs

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use anyhow::{Context, Error};
44
use build_helper::stage0_parser::{Stage0Config, VersionMetadata, parse_stage0_file};
55
use curl::easy::Easy;
66
use indexmap::IndexMap;
7+
use sha2::{Digest, Sha256};
78

89
const PATH: &str = "src/stage0";
910
const COMPILER_COMPONENTS: &[&str] = &["rustc", "rust-std", "cargo", "clippy-preview"];
@@ -13,13 +14,14 @@ struct Tool {
1314
config: Stage0Config,
1415

1516
channel: Channel,
16-
date: Option<String>,
17+
compiler_date: Option<String>,
18+
rustfmt_date: Option<String>,
1719
version: [u16; 3],
1820
checksums: IndexMap<String, String>,
1921
}
2022

2123
impl Tool {
22-
fn new(date: Option<String>) -> Result<Self, Error> {
24+
fn new(compiler_date: Option<String>, rustfmt_date: Option<String>) -> Result<Self, Error> {
2325
let channel = match std::fs::read_to_string("src/ci/channel")?.trim() {
2426
"stable" => Channel::Stable,
2527
"beta" => Channel::Beta,
@@ -38,7 +40,14 @@ impl Tool {
3840

3941
let existing = parse_stage0_file();
4042

41-
Ok(Self { channel, version, date, config: existing.config, checksums: IndexMap::new() })
43+
Ok(Self {
44+
channel,
45+
version,
46+
compiler_date,
47+
rustfmt_date,
48+
config: existing.config,
49+
checksums: IndexMap::new(),
50+
})
4251
}
4352

4453
fn update_stage0_file(mut self) -> Result<(), Error> {
@@ -78,10 +87,21 @@ impl Tool {
7887
file_content.push_str("\n");
7988

8089
let compiler = self.detect_compiler()?;
90+
file_content.push_str(&format!(
91+
"compiler_channel_manifest_hash={}\n",
92+
compiler.channel_manifest_hash
93+
));
94+
file_content.push_str(&format!("compiler_git_commit_hash={}\n", compiler.git_commit_hash));
8195
file_content.push_str(&format!("compiler_date={}\n", compiler.date));
8296
file_content.push_str(&format!("compiler_version={}\n", compiler.version));
8397

8498
if let Some(rustfmt) = self.detect_rustfmt()? {
99+
file_content.push_str(&format!(
100+
"rustfmt_channel_manifest_hash={}\n",
101+
rustfmt.channel_manifest_hash
102+
));
103+
file_content
104+
.push_str(&format!("rustfmt_git_commit_hash={}\n", rustfmt.git_commit_hash));
85105
file_content.push_str(&format!("rustfmt_date={}\n", rustfmt.date));
86106
file_content.push_str(&format!("rustfmt_version={}\n", rustfmt.version));
87107
}
@@ -112,9 +132,16 @@ impl Tool {
112132
Channel::Nightly => "beta".to_string(),
113133
};
114134

115-
let manifest = fetch_manifest(&self.config, &channel, self.date.as_deref())?;
135+
let (manifest, manifest_hash) =
136+
fetch_manifest(&self.config, &channel, self.compiler_date.as_deref())?;
116137
self.collect_checksums(&manifest, COMPILER_COMPONENTS)?;
117138
Ok(VersionMetadata {
139+
channel_manifest_hash: manifest_hash,
140+
git_commit_hash: manifest.pkg["rust"]
141+
.git_commit_hash
142+
.as_ref()
143+
.expect("invalid git_commit_hash")
144+
.into(),
118145
date: manifest.date,
119146
version: if self.channel == Channel::Nightly {
120147
"beta".to_string()
@@ -138,9 +165,19 @@ impl Tool {
138165
return Ok(None);
139166
}
140167

141-
let manifest = fetch_manifest(&self.config, "nightly", self.date.as_deref())?;
168+
let (manifest, manifest_hash) =
169+
fetch_manifest(&self.config, "nightly", self.rustfmt_date.as_deref())?;
142170
self.collect_checksums(&manifest, RUSTFMT_COMPONENTS)?;
143-
Ok(Some(VersionMetadata { date: manifest.date, version: "nightly".into() }))
171+
Ok(Some(VersionMetadata {
172+
channel_manifest_hash: manifest_hash,
173+
git_commit_hash: manifest.pkg["rust"]
174+
.git_commit_hash
175+
.as_ref()
176+
.expect("invalid git_commit_hash")
177+
.into(),
178+
date: manifest.date,
179+
version: "nightly".into(),
180+
}))
144181
}
145182

146183
fn collect_checksums(&mut self, manifest: &Manifest, components: &[&str]) -> Result<(), Error> {
@@ -164,12 +201,29 @@ impl Tool {
164201
}
165202
}
166203
}
204+
for artifact in manifest.artifacts.values() {
205+
for targets in artifact.target.values() {
206+
for target in targets {
207+
let url = target
208+
.url
209+
.strip_prefix(&prefix)
210+
.ok_or_else(|| {
211+
anyhow::anyhow!(
212+
"url doesn't start with dist server base: {}",
213+
target.url
214+
)
215+
})?
216+
.to_string();
217+
self.checksums.insert(url, target.hash_sha256.clone());
218+
}
219+
}
220+
}
167221
Ok(())
168222
}
169223
}
170224

171225
fn main() -> Result<(), Error> {
172-
let tool = Tool::new(std::env::args().nth(1))?;
226+
let tool = Tool::new(std::env::args().nth(1), std::env::args().nth(2))?;
173227
tool.update_stage0_file()?;
174228
Ok(())
175229
}
@@ -178,14 +232,21 @@ fn fetch_manifest(
178232
config: &Stage0Config,
179233
channel: &str,
180234
date: Option<&str>,
181-
) -> Result<Manifest, Error> {
235+
) -> Result<(Manifest, String), Error> {
182236
let url = if let Some(date) = date {
183237
format!("{}/dist/{}/channel-rust-{}.toml", config.dist_server, date, channel)
184238
} else {
185239
format!("{}/dist/channel-rust-{}.toml", config.dist_server, channel)
186240
};
187241

188-
Ok(toml::from_slice(&http_get(&url)?)?)
242+
let manifest_bytes = http_get(&url)?;
243+
let manifest = toml::from_slice(&manifest_bytes)?;
244+
245+
let mut sha256 = Sha256::new();
246+
sha256.update(&manifest_bytes);
247+
let manifest_hash = hex::encode(sha256.finalize());
248+
249+
Ok((manifest, manifest_hash))
189250
}
190251

191252
fn http_get(url: &str) -> Result<Vec<u8>, Error> {
@@ -215,11 +276,14 @@ enum Channel {
215276
struct Manifest {
216277
date: String,
217278
pkg: IndexMap<String, ManifestPackage>,
279+
artifacts: IndexMap<String, ManifestArtifact>,
218280
}
219281

220282
#[derive(Debug, serde::Serialize, serde::Deserialize)]
221283
struct ManifestPackage {
222284
version: String,
285+
#[serde(default)]
286+
git_commit_hash: Option<String>,
223287
target: IndexMap<String, ManifestTargetPackage>,
224288
}
225289

@@ -230,3 +294,15 @@ struct ManifestTargetPackage {
230294
xz_url: Option<String>,
231295
xz_hash: Option<String>,
232296
}
297+
298+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
299+
struct ManifestArtifact {
300+
target: IndexMap<String, Vec<ManifestTargetArtifact>>,
301+
}
302+
303+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
304+
#[serde(rename_all = "kebab-case")]
305+
struct ManifestTargetArtifact {
306+
url: String,
307+
hash_sha256: String,
308+
}

0 commit comments

Comments
 (0)