@@ -197,6 +197,7 @@ const PREFIX_TEMPLATE: &str = "{prefix}";
197197const LOWER_PREFIX_TEMPLATE : & str = "{lowerprefix}" ;
198198const CHECKSUM_TEMPLATE : & str = "{sha256-checksum}" ;
199199const MAX_UNPACK_SIZE : u64 = 512 * 1024 * 1024 ;
200+ const MAX_COMPRESSION_RATIO : usize = 20 ; // 20:1
200201
201202/// A "source" for a local (see `local::LocalRegistry`) or remote (see
202203/// `remote::RemoteRegistry`) registry.
@@ -627,9 +628,12 @@ impl<'cfg> RegistrySource<'cfg> {
627628 return Ok ( unpack_dir. to_path_buf ( ) ) ;
628629 }
629630 }
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+ } ;
633637 let prefix = unpack_dir. file_name ( ) . unwrap ( ) ;
634638 let parent = unpack_dir. parent ( ) . unwrap ( ) ;
635639 for entry in tar. entries ( ) ? {
@@ -851,18 +855,47 @@ impl<'cfg> Source for RegistrySource<'cfg> {
851855 }
852856}
853857
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 )
860882 . unwrap ( )
861883 . parse ( )
862884 . expect ( "a max unpack size in bytes" )
863885 } else {
864886 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 )
866899}
867900
868901fn make_dep_prefix ( name : & str ) -> String {
0 commit comments