@@ -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.
@@ -627,9 +628,12 @@ impl<'cfg> RegistrySource<'cfg> {
627
628
return Ok ( unpack_dir. to_path_buf ( ) ) ;
628
629
}
629
630
}
630
- let gz = GzDecoder :: new ( tarball) ;
631
- let gz = LimitErrorReader :: new ( gz, max_unpack_size ( ) ) ;
632
- let mut tar = Archive :: new ( gz) ;
631
+ let mut tar = {
632
+ let size_limit = max_unpack_size ( tarball. metadata ( ) ?. len ( ) ) ;
633
+ let gz = GzDecoder :: new ( tarball) ;
634
+ let gz = LimitErrorReader :: new ( gz, size_limit) ;
635
+ Archive :: new ( gz)
636
+ } ;
633
637
let prefix = unpack_dir. file_name ( ) . unwrap ( ) ;
634
638
let parent = unpack_dir. parent ( ) . unwrap ( ) ;
635
639
for entry in tar. entries ( ) ? {
@@ -851,18 +855,47 @@ impl<'cfg> Source for RegistrySource<'cfg> {
851
855
}
852
856
}
853
857
854
- /// For integration test only.
855
- #[ inline]
856
- fn max_unpack_size ( ) -> u64 {
857
- const VAR : & str = "__CARGO_TEST_MAX_UNPACK_SIZE" ;
858
- if cfg ! ( debug_assertions) && std:: env:: var ( VAR ) . is_ok ( ) {
859
- std:: env:: var ( VAR )
858
+ /// Get the maximum upack size that Cargo permits
859
+ /// based on a given `size of your compressed file.
860
+ ///
861
+ /// Returns the larger one between `size * max compression ratio`
862
+ /// and a fixed max unpacked size.
863
+ ///
864
+ /// In reality, the compression ratio usually falls in the range of 2:1 to 10:1.
865
+ /// We choose 20:1 to cover almost all possible cases hopefully.
866
+ /// Any ratio higher than this is considered as a zip bomb.
867
+ ///
868
+ /// In the future we might want to introduce a configurable size.
869
+ ///
870
+ /// Some of the real world data from common compression algorithms:
871
+ ///
872
+ /// * <https://www.zlib.net/zlib_tech.html>
873
+ /// * <https://cran.r-project.org/web/packages/brotli/vignettes/brotli-2015-09-22.pdf>
874
+ /// * <https://blog.cloudflare.com/results-experimenting-brotli/>
875
+ /// * <https://tukaani.org/lzma/benchmarks.html>
876
+ fn max_unpack_size ( size : u64 ) -> u64 {
877
+ const SIZE_VAR : & str = "__CARGO_TEST_MAX_UNPACK_SIZE" ;
878
+ const RATIO_VAR : & str = "__CARGO_TEST_MAX_UNPACK_RATIO" ;
879
+ let max_unpack_size = if cfg ! ( debug_assertions) && std:: env:: var ( SIZE_VAR ) . is_ok ( ) {
880
+ // For integration test only.
881
+ std:: env:: var ( SIZE_VAR )
860
882
. unwrap ( )
861
883
. parse ( )
862
884
. expect ( "a max unpack size in bytes" )
863
885
} else {
864
886
MAX_UNPACK_SIZE
865
- }
887
+ } ;
888
+ let max_compression_ratio = if cfg ! ( debug_assertions) && std:: env:: var ( RATIO_VAR ) . is_ok ( ) {
889
+ // For integration test only.
890
+ std:: env:: var ( RATIO_VAR )
891
+ . unwrap ( )
892
+ . parse ( )
893
+ . expect ( "a max compresssion ratio in bytes" )
894
+ } else {
895
+ MAX_COMPRESSION_RATIO
896
+ } ;
897
+
898
+ u64:: max ( max_unpack_size, size * max_compression_ratio as u64 )
866
899
}
867
900
868
901
fn make_dep_prefix ( name : & str ) -> String {
0 commit comments