Skip to content

Commit e353933

Browse files
authored
Merge pull request #34604 from arielb1/metadata-hash-beta
More robust metadata lock - beta edition
2 parents a72ac96 + 8da25e8 commit e353933

15 files changed

+106
-58
lines changed

src/librustc_metadata/common.rs

+4
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,7 @@ pub fn rustc_version() -> String {
252252
}
253253

254254
pub const tag_panic_strategy: usize = 0x114;
255+
256+
// NB: increment this if you change the format of metadata such that
257+
// rustc_version can't be found.
258+
pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2];

src/librustc_metadata/creader.rs

+2-21
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
//! Validates all used crates and extern libraries and loads their metadata
1414
15-
use common::rustc_version;
1615
use cstore::{self, CStore, CrateSource, MetadataBlob};
1716
use decoder;
1817
use loader::{self, CratePaths};
@@ -234,25 +233,6 @@ impl<'a> CrateReader<'a> {
234233
return ret;
235234
}
236235

237-
fn verify_rustc_version(&self,
238-
name: &str,
239-
span: Span,
240-
metadata: &MetadataBlob) {
241-
let crate_rustc_version = decoder::crate_rustc_version(metadata.as_slice());
242-
if crate_rustc_version != Some(rustc_version()) {
243-
let mut err = struct_span_fatal!(self.sess, span, E0514,
244-
"the crate `{}` has been compiled with {}, which is \
245-
incompatible with this version of rustc",
246-
name,
247-
crate_rustc_version
248-
.as_ref().map(|s| &**s)
249-
.unwrap_or("an old version of rustc"));
250-
err.help("consider removing the compiled binaries and recompiling \
251-
with your current version of rustc");
252-
err.emit();
253-
}
254-
}
255-
256236
fn verify_no_symbol_conflicts(&self,
257237
span: Span,
258238
metadata: &MetadataBlob) {
@@ -294,7 +274,6 @@ impl<'a> CrateReader<'a> {
294274
explicitly_linked: bool)
295275
-> (ast::CrateNum, Rc<cstore::crate_metadata>,
296276
cstore::CrateSource) {
297-
self.verify_rustc_version(name, span, &lib.metadata);
298277
self.verify_no_symbol_conflicts(span, &lib.metadata);
299278

300279
// Claim this crate number and cache it
@@ -379,6 +358,7 @@ impl<'a> CrateReader<'a> {
379358
rejected_via_hash: vec!(),
380359
rejected_via_triple: vec!(),
381360
rejected_via_kind: vec!(),
361+
rejected_via_version: vec!(),
382362
should_match_name: true,
383363
};
384364
match self.load(&mut load_ctxt) {
@@ -506,6 +486,7 @@ impl<'a> CrateReader<'a> {
506486
rejected_via_hash: vec!(),
507487
rejected_via_triple: vec!(),
508488
rejected_via_kind: vec!(),
489+
rejected_via_version: vec!(),
509490
should_match_name: true,
510491
};
511492
let library = self.load(&mut load_ctxt).or_else(|| {

src/librustc_metadata/csearch.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
use cstore;
12+
use common;
1213
use decoder;
1314
use encoder;
1415
use loader;
@@ -587,7 +588,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
587588

588589
fn metadata_encoding_version(&self) -> &[u8]
589590
{
590-
encoder::metadata_encoding_version
591+
common::metadata_encoding_version
591592
}
592593

593594
/// Returns a map from a sufficiently visible external item (i.e. an external item that is

src/librustc_metadata/cstore.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
pub use self::MetadataBlob::*;
1717

18+
use common;
1819
use creader;
1920
use decoder;
2021
use index;
@@ -311,20 +312,25 @@ impl crate_metadata {
311312
}
312313

313314
impl MetadataBlob {
314-
pub fn as_slice<'a>(&'a self) -> &'a [u8] {
315-
let slice = match *self {
315+
pub fn as_slice_raw<'a>(&'a self) -> &'a [u8] {
316+
match *self {
316317
MetadataVec(ref vec) => &vec[..],
317318
MetadataArchive(ref ar) => ar.as_slice(),
318-
};
319-
if slice.len() < 4 {
319+
}
320+
}
321+
322+
pub fn as_slice<'a>(&'a self) -> &'a [u8] {
323+
let slice = self.as_slice_raw();
324+
let len_offset = 4 + common::metadata_encoding_version.len();
325+
if slice.len() < len_offset+4 {
320326
&[] // corrupt metadata
321327
} else {
322-
let len = (((slice[0] as u32) << 24) |
323-
((slice[1] as u32) << 16) |
324-
((slice[2] as u32) << 8) |
325-
((slice[3] as u32) << 0)) as usize;
326-
if len + 4 <= slice.len() {
327-
&slice[4.. len + 4]
328+
let len = (((slice[len_offset+0] as u32) << 24) |
329+
((slice[len_offset+1] as u32) << 16) |
330+
((slice[len_offset+2] as u32) << 8) |
331+
((slice[len_offset+3] as u32) << 0)) as usize;
332+
if len <= slice.len() - 4 - len_offset {
333+
&slice[len_offset + 4..len_offset + len + 4]
328334
} else {
329335
&[] // corrupt or old metadata
330336
}

src/librustc_metadata/encoder.rs

+19-10
Original file line numberDiff line numberDiff line change
@@ -1851,10 +1851,6 @@ fn encode_panic_strategy(rbml_w: &mut Encoder, ecx: &EncodeContext) {
18511851
}
18521852
}
18531853

1854-
// NB: Increment this as you change the metadata encoding version.
1855-
#[allow(non_upper_case_globals)]
1856-
pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2 ];
1857-
18581854
pub fn encode_metadata(ecx: EncodeContext, krate: &hir::Crate) -> Vec<u8> {
18591855
let mut wr = Cursor::new(Vec::new());
18601856

@@ -1888,12 +1884,25 @@ pub fn encode_metadata(ecx: EncodeContext, krate: &hir::Crate) -> Vec<u8> {
18881884
// the length of the metadata to the start of the metadata. Later on this
18891885
// will allow us to slice the metadata to the precise length that we just
18901886
// generated regardless of trailing bytes that end up in it.
1891-
let len = v.len() as u32;
1892-
v.insert(0, (len >> 0) as u8);
1893-
v.insert(0, (len >> 8) as u8);
1894-
v.insert(0, (len >> 16) as u8);
1895-
v.insert(0, (len >> 24) as u8);
1896-
return v;
1887+
//
1888+
// We also need to store the metadata encoding version here, because
1889+
// rlibs don't have it. To get older versions of rustc to ignore
1890+
// this metadata, there are 4 zero bytes at the start, which are
1891+
// treated as a length of 0 by old compilers.
1892+
1893+
let len = v.len();
1894+
let mut result = vec![];
1895+
result.push(0);
1896+
result.push(0);
1897+
result.push(0);
1898+
result.push(0);
1899+
result.extend(metadata_encoding_version.iter().cloned());
1900+
result.push((len >> 24) as u8);
1901+
result.push((len >> 16) as u8);
1902+
result.push((len >> 8) as u8);
1903+
result.push((len >> 0) as u8);
1904+
result.extend(v);
1905+
result
18971906
}
18981907

18991908
fn encode_metadata_inner(rbml_w: &mut Encoder,

src/librustc_metadata/loader.rs

+54-7
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@
213213
//! metadata::loader or metadata::creader for all the juicy details!
214214
215215
use cstore::{MetadataBlob, MetadataVec, MetadataArchive};
216+
use common::{metadata_encoding_version, rustc_version};
216217
use decoder;
217-
use encoder;
218218

219219
use rustc::hir::svh::Svh;
220220
use rustc::session::Session;
@@ -260,6 +260,7 @@ pub struct Context<'a> {
260260
pub rejected_via_hash: Vec<CrateMismatch>,
261261
pub rejected_via_triple: Vec<CrateMismatch>,
262262
pub rejected_via_kind: Vec<CrateMismatch>,
263+
pub rejected_via_version: Vec<CrateMismatch>,
263264
pub should_match_name: bool,
264265
}
265266

@@ -336,6 +337,10 @@ impl<'a> Context<'a> {
336337
struct_span_err!(self.sess, self.span, E0462,
337338
"found staticlib `{}` instead of rlib or dylib{}",
338339
self.ident, add)
340+
} else if !self.rejected_via_version.is_empty() {
341+
struct_span_err!(self.sess, self.span, E0514,
342+
"found crate `{}` compiled by an incompatible version of rustc{}",
343+
self.ident, add)
339344
} else {
340345
struct_span_err!(self.sess, self.span, E0463,
341346
"can't find crate for `{}`{}",
@@ -350,7 +355,7 @@ impl<'a> Context<'a> {
350355
}
351356
}
352357
if !self.rejected_via_hash.is_empty() {
353-
err.note("perhaps this crate needs to be recompiled?");
358+
err.note("perhaps that crate needs to be recompiled?");
354359
let mismatches = self.rejected_via_hash.iter();
355360
for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() {
356361
err.note(&format!("crate `{}` path #{}: {}",
@@ -367,13 +372,22 @@ impl<'a> Context<'a> {
367372
}
368373
}
369374
if !self.rejected_via_kind.is_empty() {
370-
err.help("please recompile this crate using --crate-type lib");
375+
err.help("please recompile that crate using --crate-type lib");
371376
let mismatches = self.rejected_via_kind.iter();
372377
for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
373378
err.note(&format!("crate `{}` path #{}: {}",
374379
self.ident, i+1, path.display()));
375380
}
376381
}
382+
if !self.rejected_via_version.is_empty() {
383+
err.help(&format!("please recompile that crate using this compiler ({})",
384+
rustc_version()));
385+
let mismatches = self.rejected_via_version.iter();
386+
for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() {
387+
err.note(&format!("crate `{}` path #{}: {} compiled by {:?}",
388+
self.ident, i+1, path.display(), got));
389+
}
390+
}
377391

378392
err.emit();
379393
self.sess.abort_if_errors();
@@ -591,6 +605,17 @@ impl<'a> Context<'a> {
591605
}
592606

593607
fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> Option<Svh> {
608+
let crate_rustc_version = decoder::crate_rustc_version(crate_data);
609+
if crate_rustc_version != Some(rustc_version()) {
610+
let message = crate_rustc_version.unwrap_or(format!("an unknown compiler"));
611+
info!("Rejecting via version: expected {} got {}", rustc_version(), message);
612+
self.rejected_via_version.push(CrateMismatch {
613+
path: libpath.to_path_buf(),
614+
got: message
615+
});
616+
return None;
617+
}
618+
594619
if self.should_match_name {
595620
match decoder::maybe_get_crate_name(crate_data) {
596621
Some(ref name) if self.crate_name == *name => {}
@@ -742,6 +767,21 @@ impl ArchiveMetadata {
742767
pub fn as_slice<'a>(&'a self) -> &'a [u8] { unsafe { &*self.data } }
743768
}
744769

770+
fn verify_decompressed_encoding_version(blob: &MetadataBlob, filename: &Path)
771+
-> Result<(), String>
772+
{
773+
let data = blob.as_slice_raw();
774+
if data.len() < 4+metadata_encoding_version.len() ||
775+
!<[u8]>::eq(&data[..4], &[0, 0, 0, 0]) ||
776+
&data[4..4+metadata_encoding_version.len()] != metadata_encoding_version
777+
{
778+
Err((format!("incompatible metadata version found: '{}'",
779+
filename.display())))
780+
} else {
781+
Ok(())
782+
}
783+
}
784+
745785
// Just a small wrapper to time how long reading metadata takes.
746786
fn get_metadata_section(target: &Target, flavor: CrateFlavor, filename: &Path)
747787
-> Result<MetadataBlob, String> {
@@ -772,7 +812,10 @@ fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Pat
772812
return match ArchiveMetadata::new(archive).map(|ar| MetadataArchive(ar)) {
773813
None => Err(format!("failed to read rlib metadata: '{}'",
774814
filename.display())),
775-
Some(blob) => Ok(blob)
815+
Some(blob) => {
816+
try!(verify_decompressed_encoding_version(&blob, filename));
817+
Ok(blob)
818+
}
776819
};
777820
}
778821
unsafe {
@@ -801,12 +844,12 @@ fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Pat
801844
let cbuf = llvm::LLVMGetSectionContents(si.llsi);
802845
let csz = llvm::LLVMGetSectionSize(si.llsi) as usize;
803846
let cvbuf: *const u8 = cbuf as *const u8;
804-
let vlen = encoder::metadata_encoding_version.len();
847+
let vlen = metadata_encoding_version.len();
805848
debug!("checking {} bytes of metadata-version stamp",
806849
vlen);
807850
let minsz = cmp::min(vlen, csz);
808851
let buf0 = slice::from_raw_parts(cvbuf, minsz);
809-
let version_ok = buf0 == encoder::metadata_encoding_version;
852+
let version_ok = buf0 == metadata_encoding_version;
810853
if !version_ok {
811854
return Err((format!("incompatible metadata version found: '{}'",
812855
filename.display())));
@@ -817,7 +860,11 @@ fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Pat
817860
csz - vlen);
818861
let bytes = slice::from_raw_parts(cvbuf1, csz - vlen);
819862
match flate::inflate_bytes(bytes) {
820-
Ok(inflated) => return Ok(MetadataVec(inflated)),
863+
Ok(inflated) => {
864+
let blob = MetadataVec(inflated);
865+
try!(verify_decompressed_encoding_version(&blob, filename));
866+
return Ok(blob);
867+
}
821868
Err(_) => {}
822869
}
823870
}

src/test/compile-fail/changing-crates.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
extern crate a;
1919
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
20-
//~| NOTE: perhaps this crate needs to be recompiled
20+
//~| NOTE: perhaps that crate needs to be recompiled
2121
//~| NOTE: crate `a` path #1:
2222
//~| NOTE: crate `b` path #1:
2323

src/test/compile-fail/svh-change-lit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
extern crate a;
1919
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
20-
//~| NOTE: perhaps this crate needs to be recompiled
20+
//~| NOTE: perhaps that crate needs to be recompiled
2121
//~| NOTE: crate `a` path #1:
2222
//~| NOTE: crate `b` path #1:
2323

src/test/compile-fail/svh-change-significant-cfg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
extern crate a;
1919
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
20-
//~| NOTE: perhaps this crate needs to be recompiled
20+
//~| NOTE: perhaps that crate needs to be recompiled
2121
//~| NOTE: crate `a` path #1:
2222
//~| NOTE: crate `b` path #1:
2323

src/test/compile-fail/svh-change-trait-bound.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
extern crate a;
1919
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
20-
//~| NOTE: perhaps this crate needs to be recompiled
20+
//~| NOTE: perhaps that crate needs to be recompiled
2121
//~| NOTE: crate `a` path #1:
2222
//~| NOTE: crate `b` path #1:
2323

src/test/compile-fail/svh-change-type-arg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
extern crate a;
1919
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
20-
//~| NOTE: perhaps this crate needs to be recompiled
20+
//~| NOTE: perhaps that crate needs to be recompiled
2121
//~| NOTE: crate `a` path #1:
2222
//~| NOTE: crate `b` path #1:
2323

src/test/compile-fail/svh-change-type-ret.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
extern crate a;
1919
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
20-
//~| NOTE: perhaps this crate needs to be recompiled
20+
//~| NOTE: perhaps that crate needs to be recompiled
2121
//~| NOTE: crate `a` path #1:
2222
//~| NOTE: crate `b` path #1:
2323

src/test/compile-fail/svh-change-type-static.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
extern crate a;
1919
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
20-
//~| NOTE: perhaps this crate needs to be recompiled
20+
//~| NOTE: perhaps that crate needs to be recompiled
2121
//~| NOTE: crate `a` path #1:
2222
//~| NOTE: crate `b` path #1:
2323

src/test/compile-fail/svh-use-trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
2323
extern crate uta;
2424
extern crate utb; //~ ERROR: found possibly newer version of crate `uta` which `utb` depends
25-
//~| NOTE: perhaps this crate needs to be recompiled?
25+
//~| NOTE: perhaps that crate needs to be recompiled?
2626
//~| NOTE: crate `uta` path #1:
2727
//~| NOTE: crate `utb` path #1:
2828

src/test/run-make/many-crates-but-no-match/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ all:
2828
# Ensure crateC fails to compile since A1 is "missing" and A2/A3 hashes do not match
2929
$(RUSTC) -L $(A2) -L $(A3) crateC.rs >$(LOG) 2>&1 || true
3030
grep "error: found possibly newer version of crate \`crateA\` which \`crateB\` depends on" $(LOG)
31-
grep "note: perhaps this crate needs to be recompiled?" $(LOG)
31+
grep "note: perhaps that crate needs to be recompiled?" $(LOG)
3232
grep "note: crate \`crateA\` path #1:" $(LOG)
3333
grep "note: crate \`crateA\` path #2:" $(LOG)
3434
grep "note: crate \`crateB\` path #1:" $(LOG)

0 commit comments

Comments
 (0)