diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index c9c414e50..0c52d530e 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -618,7 +618,7 @@ impl<'cfg> RegistrySource<'cfg> { } } let gz = GzDecoder::new(tarball); - let gz = LimitErrorReader::new(gz, MAX_UNPACK_SIZE); + let gz = LimitErrorReader::new(gz, max_unpack_size()); let mut tar = Archive::new(gz); let prefix = unpack_dir.file_name().unwrap(); let parent = unpack_dir.parent().unwrap(); @@ -835,6 +835,20 @@ impl<'cfg> Source for RegistrySource<'cfg> { } } +/// For integration test only. +#[inline] +fn max_unpack_size() -> u64 { + const VAR: &str = "__CARGO_TEST_MAX_UNPACK_SIZE"; + if cfg!(debug_assertions) && std::env::var(VAR).is_ok() { + std::env::var(VAR) + .unwrap() + .parse() + .expect("a max unpack size in bytes") + } else { + MAX_UNPACK_SIZE + } +} + fn make_dep_prefix(name: &str) -> String { match name.len() { 1 => String::from("1"), diff --git a/src/cargo/util/io.rs b/src/cargo/util/io.rs index f62672db0..60f3ffe05 100644 --- a/src/cargo/util/io.rs +++ b/src/cargo/util/io.rs @@ -25,3 +25,27 @@ impl Read for LimitErrorReader { } } +#[cfg(test)] +mod tests { + use super::LimitErrorReader; + + use std::io::Read; + + #[test] + fn under_the_limit() { + let buf = &[1; 7][..]; + let mut r = LimitErrorReader::new(buf, 8); + let mut out = Vec::new(); + assert!(matches!(r.read_to_end(&mut out), Ok(7))); + assert_eq!(buf, out.as_slice()); + } + + #[test] + #[should_panic = "maximum limit reached when reading"] + fn over_the_limit() { + let buf = &[1; 8][..]; + let mut r = LimitErrorReader::new(buf, 8); + let mut out = Vec::new(); + r.read_to_end(&mut out).unwrap(); + } +} diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 4fcc4ffbe..a5032d2ac 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -2697,3 +2697,45 @@ fn http_requires_trailing_slash() { .with_stderr("[ERROR] registry url must end in a slash `/`: sparse+https://index.crates.io") .run() } + +#[cargo_test] +fn reach_max_unpack_size() { + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("build") + .env("__CARGO_TEST_MAX_UNPACK_SIZE", "8") // hit 8 bytes limit and boom! + .with_status(101) + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[ERROR] failed to download replaced source registry `crates-io` + +Caused by: + failed to unpack package `bar v0.0.1 (registry `dummy-registry`)` + +Caused by: + failed to iterate over archive + +Caused by: + maximum limit reached when reading +", + ) + .run(); +}