@@ -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.
@@ -617,9 +618,12 @@ impl<'cfg> RegistrySource<'cfg> {
617618 return Ok ( unpack_dir. to_path_buf ( ) ) ;
618619 }
619620 }
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+ } ;
623627 let prefix = unpack_dir. file_name ( ) . unwrap ( ) ;
624628 let parent = unpack_dir. parent ( ) . unwrap ( ) ;
625629 for entry in tar. entries ( ) ? {
@@ -835,18 +839,47 @@ impl<'cfg> Source for RegistrySource<'cfg> {
835839 }
836840}
837841
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 )
844866 . unwrap ( )
845867 . parse ( )
846868 . expect ( "a max unpack size in bytes" )
847869 } else {
848870 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 )
850883}
851884
852885fn make_dep_prefix ( name : & str ) -> String {
0 commit comments