Skip to content

Commit a542344

Browse files
committed
Protect crate metadata from corruption via SHA-256 hash
We now compute a SHA-256 of the raw (encoded) crate metadata, and append it to the final crate metadata that we store on disk. When we load the metadata, we compute the hash from the metadata blob, and verify that matches the hash stored at the end of the blob. This allows us to detect on-disk corruption of the metadata file, which might later cause a build failure much later in compilation. If anyone is manually editing crate metadata on-disk, they will need to re-compute and modify the hash at the end of the blob. This will allow us to determine whether or not crate metadata corruption is causing some of the unusual incr-comp failures we've been seeing. The incremental compilation data itself will be hashed in a follow-up PR.
1 parent 4498e30 commit a542344

File tree

5 files changed

+34
-5
lines changed

5 files changed

+34
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3994,6 +3994,7 @@ dependencies = [
39943994
"rustc_session",
39953995
"rustc_span",
39963996
"rustc_target",
3997+
"sha2",
39973998
"smallvec",
39983999
"snap",
39994000
"tracing",

compiler/rustc_metadata/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ doctest = false
1010
libc = "0.2"
1111
snap = "1"
1212
tracing = "0.1"
13+
sha2 = "0.9"
1314
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
1415
rustc_middle = { path = "../rustc_middle" }
1516
rustc_attr = { path = "../rustc_attr" }

compiler/rustc_metadata/src/locator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ fn get_metadata_section(
773773
rustc_erase_owner!(OwningRef::new(mmap).map_owner_box())
774774
}
775775
};
776-
let blob = MetadataBlob::new(raw_bytes);
776+
let blob = MetadataBlob::new(raw_bytes, filename);
777777
if blob.is_compatible() {
778778
Ok(blob)
779779
} else {

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use rustc_span::symbol::{sym, Ident, Symbol};
3636
use rustc_span::{self, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP};
3737

3838
use proc_macro::bridge::client::ProcMacro;
39+
use sha2::{Digest, Sha256};
3940
use std::io;
4041
use std::mem;
4142
use std::num::NonZeroUsize;
@@ -583,8 +584,10 @@ where
583584
implement_ty_decoder!(DecodeContext<'a, 'tcx>);
584585

585586
impl MetadataBlob {
586-
crate fn new(metadata_ref: MetadataRef) -> MetadataBlob {
587-
MetadataBlob(metadata_ref)
587+
crate fn new(metadata_ref: MetadataRef, filename: &Path) -> MetadataBlob {
588+
let blob = MetadataBlob(metadata_ref);
589+
blob.check_hash(filename);
590+
blob
588591
}
589592

590593
crate fn is_compatible(&self) -> bool {
@@ -596,6 +599,23 @@ impl MetadataBlob {
596599
.decode(self)
597600
}
598601

602+
// Hashes the entire contents of the metadata blob,
603+
// panicking if the computed hash is not equal to
604+
// the original hash stored in the file.
605+
fn check_hash(&self, filename: &Path) {
606+
// We store our 32-byte (256-bit) SHA256 hash at
607+
// the end of the file
608+
let hash_offset = self.raw_bytes().len() - 32;
609+
let stored_hash = &self.raw_bytes()[hash_offset..];
610+
let recomputed_hash = Sha256::digest(&self.raw_bytes()[..hash_offset]);
611+
if stored_hash != &*recomputed_hash {
612+
panic!(
613+
"Expected metadata hash {:?}, found {:?} for file {:?}",
614+
stored_hash, recomputed_hash, filename
615+
);
616+
}
617+
}
618+
599619
crate fn get_root(&self) -> CrateRoot<'tcx> {
600620
let slice = self.raw_bytes();
601621
let offset = METADATA_HEADER.len();

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use rustc_span::{
3535
RealFileName,
3636
};
3737
use rustc_target::abi::VariantIdx;
38+
use sha2::{Digest, Sha256};
3839
use std::hash::Hash;
3940
use std::num::NonZeroUsize;
4041
use std::path::Path;
@@ -2144,7 +2145,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata {
21442145
// culminating in the `CrateRoot` which points to all of it.
21452146
let root = ecx.encode_crate_root();
21462147

2147-
let mut result = ecx.opaque.into_inner();
2148+
let result = &mut ecx.opaque.data;
21482149

21492150
// Encode the root position.
21502151
let header = METADATA_HEADER.len();
@@ -2154,5 +2155,11 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata {
21542155
result[header + 2] = (pos >> 8) as u8;
21552156
result[header + 3] = (pos >> 0) as u8;
21562157

2157-
EncodedMetadata { raw_data: result }
2158+
// Encode a hash of the file contents. We check
2159+
// this when decoding the blob, to protect
2160+
// against file corruption.
2161+
let hash = Sha256::digest(&result);
2162+
ecx.opaque.emit_raw_bytes(&hash).unwrap();
2163+
2164+
EncodedMetadata { raw_data: ecx.opaque.into_inner() }
21582165
}

0 commit comments

Comments
 (0)