@@ -197,6 +197,7 @@ const PREFIX_TEMPLATE: &str = "{prefix}";
197
197
const LOWER_PREFIX_TEMPLATE : & str = "{lowerprefix}" ;
198
198
const CHECKSUM_TEMPLATE : & str = "{sha256-checksum}" ;
199
199
const MAX_UNPACK_SIZE : u64 = 512 * 1024 * 1024 ;
200
+ const MAX_COMPRESSION_RATIO : usize = 20 ; // 20:1
200
201
201
202
/// A "source" for a local (see `local::LocalRegistry`) or remote (see
202
203
/// `remote::RemoteRegistry`) registry.
@@ -617,9 +618,12 @@ impl<'cfg> RegistrySource<'cfg> {
617
618
return Ok ( unpack_dir. to_path_buf ( ) ) ;
618
619
}
619
620
}
620
- let gz = GzDecoder :: new ( tarball) ;
621
- let gz = LimitErrorReader :: new ( gz, max_unpack_size ( ) ) ;
622
- let mut tar = Archive :: new ( gz) ;
621
+ let mut tar = {
622
+ let size_limit = max_unpack_size ( tarball. metadata ( ) ?. len ( ) ) ;
623
+ let gz = GzDecoder :: new ( tarball) ;
624
+ let gz = LimitErrorReader :: new ( gz, size_limit) ;
625
+ Archive :: new ( gz)
626
+ } ;
623
627
let prefix = unpack_dir. file_name ( ) . unwrap ( ) ;
624
628
let parent = unpack_dir. parent ( ) . unwrap ( ) ;
625
629
for entry in tar. entries ( ) ? {
@@ -835,18 +839,47 @@ impl<'cfg> Source for RegistrySource<'cfg> {
835
839
}
836
840
}
837
841
838
- /// For integration test only.
839
- #[ inline]
840
- fn max_unpack_size ( ) -> u64 {
841
- const VAR : & str = "__CARGO_TEST_MAX_UNPACK_SIZE" ;
842
- if cfg ! ( debug_assertions) && std:: env:: var ( VAR ) . is_ok ( ) {
843
- std:: env:: var ( VAR )
842
+ /// Get the maximum upack size that Cargo permits
843
+ /// based on a given `size of your compressed file.
844
+ ///
845
+ /// Returns the larger one between `size * max compression ratio`
846
+ /// and a fixed max unpacked size.
847
+ ///
848
+ /// In reality, the compression ratio usually falls in the range of 2:1 to 10:1.
849
+ /// We choose 20:1 to cover almost all possible cases hopefully.
850
+ /// Any ratio higher than this is considered as a zip bomb.
851
+ ///
852
+ /// In the future we might want to introduce a configurable size.
853
+ ///
854
+ /// Some of the real world data from common compression algorithms:
855
+ ///
856
+ /// * <https://www.zlib.net/zlib_tech.html>
857
+ /// * <https://cran.r-project.org/web/packages/brotli/vignettes/brotli-2015-09-22.pdf>
858
+ /// * <https://blog.cloudflare.com/results-experimenting-brotli/>
859
+ /// * <https://tukaani.org/lzma/benchmarks.html>
860
+ fn max_unpack_size ( size : u64 ) -> u64 {
861
+ const SIZE_VAR : & str = "__CARGO_TEST_MAX_UNPACK_SIZE" ;
862
+ const RATIO_VAR : & str = "__CARGO_TEST_MAX_UNPACK_RATIO" ;
863
+ let max_unpack_size = if cfg ! ( debug_assertions) && std:: env:: var ( SIZE_VAR ) . is_ok ( ) {
864
+ // For integration test only.
865
+ std:: env:: var ( SIZE_VAR )
844
866
. unwrap ( )
845
867
. parse ( )
846
868
. expect ( "a max unpack size in bytes" )
847
869
} else {
848
870
MAX_UNPACK_SIZE
849
- }
871
+ } ;
872
+ let max_compression_ratio = if cfg ! ( debug_assertions) && std:: env:: var ( RATIO_VAR ) . is_ok ( ) {
873
+ // For integration test only.
874
+ std:: env:: var ( RATIO_VAR )
875
+ . unwrap ( )
876
+ . parse ( )
877
+ . expect ( "a max compresssion ratio in bytes" )
878
+ } else {
879
+ MAX_COMPRESSION_RATIO
880
+ } ;
881
+
882
+ u64:: max ( max_unpack_size, size * max_compression_ratio as u64 )
850
883
}
851
884
852
885
fn make_dep_prefix ( name : & str ) -> String {
0 commit comments