mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
aware of compression ratio for unpack size limit
This commit is contained in:
parent
9286a1beba
commit
de7cd31eac
@ -197,6 +197,7 @@ const PREFIX_TEMPLATE: &str = "{prefix}";
|
|||||||
const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}";
|
const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}";
|
||||||
const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}";
|
const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}";
|
||||||
const MAX_UNPACK_SIZE: u64 = 512 * 1024 * 1024;
|
const MAX_UNPACK_SIZE: u64 = 512 * 1024 * 1024;
|
||||||
|
const MAX_COMPRESSION_RATIO: usize = 20; // 20:1
|
||||||
|
|
||||||
/// A "source" for a local (see `local::LocalRegistry`) or remote (see
|
/// A "source" for a local (see `local::LocalRegistry`) or remote (see
|
||||||
/// `remote::RemoteRegistry`) registry.
|
/// `remote::RemoteRegistry`) registry.
|
||||||
@ -617,9 +618,12 @@ impl<'cfg> RegistrySource<'cfg> {
|
|||||||
return Ok(unpack_dir.to_path_buf());
|
return Ok(unpack_dir.to_path_buf());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let gz = GzDecoder::new(tarball);
|
let mut tar = {
|
||||||
let gz = LimitErrorReader::new(gz, max_unpack_size());
|
let size_limit = max_unpack_size(tarball.metadata()?.len());
|
||||||
let mut tar = Archive::new(gz);
|
let gz = GzDecoder::new(tarball);
|
||||||
|
let gz = LimitErrorReader::new(gz, size_limit);
|
||||||
|
Archive::new(gz)
|
||||||
|
};
|
||||||
let prefix = unpack_dir.file_name().unwrap();
|
let prefix = unpack_dir.file_name().unwrap();
|
||||||
let parent = unpack_dir.parent().unwrap();
|
let parent = unpack_dir.parent().unwrap();
|
||||||
for entry in tar.entries()? {
|
for entry in tar.entries()? {
|
||||||
@ -835,18 +839,47 @@ impl<'cfg> Source for RegistrySource<'cfg> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For integration test only.
|
/// Get the maximum upack size that Cargo permits
|
||||||
#[inline]
|
/// based on a given `size of your compressed file.
|
||||||
fn max_unpack_size() -> u64 {
|
///
|
||||||
const VAR: &str = "__CARGO_TEST_MAX_UNPACK_SIZE";
|
/// Returns the larger one between `size * max compression ratio`
|
||||||
if cfg!(debug_assertions) && std::env::var(VAR).is_ok() {
|
/// and a fixed max unpacked size.
|
||||||
std::env::var(VAR)
|
///
|
||||||
|
/// In reality, the compression ratio usually falls in the range of 2:1 to 10:1.
|
||||||
|
/// We choose 20:1 to cover almost all possible cases hopefully.
|
||||||
|
/// Any ratio higher than this is considered as a zip bomb.
|
||||||
|
///
|
||||||
|
/// In the future we might want to introduce a configurable size.
|
||||||
|
///
|
||||||
|
/// Some of the real world data from common compression algorithms:
|
||||||
|
///
|
||||||
|
/// * <https://www.zlib.net/zlib_tech.html>
|
||||||
|
/// * <https://cran.r-project.org/web/packages/brotli/vignettes/brotli-2015-09-22.pdf>
|
||||||
|
/// * <https://blog.cloudflare.com/results-experimenting-brotli/>
|
||||||
|
/// * <https://tukaani.org/lzma/benchmarks.html>
|
||||||
|
fn max_unpack_size(size: u64) -> u64 {
|
||||||
|
const SIZE_VAR: &str = "__CARGO_TEST_MAX_UNPACK_SIZE";
|
||||||
|
const RATIO_VAR: &str = "__CARGO_TEST_MAX_UNPACK_RATIO";
|
||||||
|
let max_unpack_size = if cfg!(debug_assertions) && std::env::var(SIZE_VAR).is_ok() {
|
||||||
|
// For integration test only.
|
||||||
|
std::env::var(SIZE_VAR)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.parse()
|
.parse()
|
||||||
.expect("a max unpack size in bytes")
|
.expect("a max unpack size in bytes")
|
||||||
} else {
|
} else {
|
||||||
MAX_UNPACK_SIZE
|
MAX_UNPACK_SIZE
|
||||||
}
|
};
|
||||||
|
let max_compression_ratio = if cfg!(debug_assertions) && std::env::var(RATIO_VAR).is_ok() {
|
||||||
|
// For integration test only.
|
||||||
|
std::env::var(RATIO_VAR)
|
||||||
|
.unwrap()
|
||||||
|
.parse()
|
||||||
|
.expect("a max compresssion ratio in bytes")
|
||||||
|
} else {
|
||||||
|
MAX_COMPRESSION_RATIO
|
||||||
|
};
|
||||||
|
|
||||||
|
u64::max(max_unpack_size, size * max_compression_ratio as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_dep_prefix(name: &str) -> String {
|
fn make_dep_prefix(name: &str) -> String {
|
||||||
|
@ -2750,10 +2750,12 @@ fn reach_max_unpack_size() {
|
|||||||
.file("src/main.rs", "fn main() {}")
|
.file("src/main.rs", "fn main() {}")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// Size of bar.crate is around 180 bytes.
|
||||||
Package::new("bar", "0.0.1").publish();
|
Package::new("bar", "0.0.1").publish();
|
||||||
|
|
||||||
p.cargo("build")
|
p.cargo("build")
|
||||||
.env("__CARGO_TEST_MAX_UNPACK_SIZE", "8") // hit 8 bytes limit and boom!
|
.env("__CARGO_TEST_MAX_UNPACK_SIZE", "8") // hit 8 bytes limit and boom!
|
||||||
|
.env("__CARGO_TEST_MAX_UNPACK_RATIO", "0")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr(
|
.with_stderr(
|
||||||
"\
|
"\
|
||||||
@ -2770,6 +2772,18 @@ Caused by:
|
|||||||
|
|
||||||
Caused by:
|
Caused by:
|
||||||
maximum limit reached when reading
|
maximum limit reached when reading
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// Restore to the default ratio and it should compile.
|
||||||
|
p.cargo("build")
|
||||||
|
.env("__CARGO_TEST_MAX_UNPACK_SIZE", "8")
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[COMPILING] bar v0.0.1
|
||||||
|
[COMPILING] foo v0.0.1 ([..])
|
||||||
|
[FINISHED] dev [..]
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user