@@ -630,60 +630,8 @@ impl<'gctx> RegistrySource<'gctx> {
630630 Err ( e) => anyhow:: bail!( "unable to read .cargo-ok file at {path:?}: {e}" ) ,
631631 }
632632 dst. create_dir ( ) ?;
633- let mut tar = {
634- let size_limit = max_unpack_size ( self . gctx , tarball. metadata ( ) ?. len ( ) ) ;
635- let gz = GzDecoder :: new ( tarball) ;
636- let gz = LimitErrorReader :: new ( gz, size_limit) ;
637- let mut tar = Archive :: new ( gz) ;
638- set_mask ( & mut tar) ;
639- tar
640- } ;
641- let mut bytes_written = 0 ;
642- let prefix = unpack_dir. file_name ( ) . unwrap ( ) ;
643- let parent = unpack_dir. parent ( ) . unwrap ( ) ;
644- for entry in tar. entries ( ) ? {
645- let mut entry = entry. context ( "failed to iterate over archive" ) ?;
646- let entry_path = entry
647- . path ( )
648- . context ( "failed to read entry path" ) ?
649- . into_owned ( ) ;
650-
651- // We're going to unpack this tarball into the global source
652- // directory, but we want to make sure that it doesn't accidentally
653- // (or maliciously) overwrite source code from other crates. Cargo
654- // itself should never generate a tarball that hits this error, and
655- // crates.io should also block uploads with these sorts of tarballs,
656- // but be extra sure by adding a check here as well.
657- if !entry_path. starts_with ( prefix) {
658- anyhow:: bail!(
659- "invalid tarball downloaded, contains \
660- a file at {:?} which isn't under {:?}",
661- entry_path,
662- prefix
663- )
664- }
665- // Prevent unpacking the lockfile from the crate itself.
666- if entry_path
667- . file_name ( )
668- . map_or ( false , |p| p == PACKAGE_SOURCE_LOCK )
669- {
670- continue ;
671- }
672- // Unpacking failed
673- bytes_written += entry. size ( ) ;
674- let mut result = entry. unpack_in ( parent) . map_err ( anyhow:: Error :: from) ;
675- if cfg ! ( windows) && restricted_names:: is_windows_reserved_path ( & entry_path) {
676- result = result. with_context ( || {
677- format ! (
678- "`{}` appears to contain a reserved Windows path, \
679- it cannot be extracted on Windows",
680- entry_path. display( )
681- )
682- } ) ;
683- }
684- result
685- . with_context ( || format ! ( "failed to unpack entry at `{}`" , entry_path. display( ) ) ) ?;
686- }
633+
634+ let bytes_written = unpack ( self . gctx , tarball, unpack_dir) ?;
687635
688636 // Now that we've finished unpacking, create and write to the lock file to indicate that
689637 // unpacking was successful.
@@ -1046,3 +994,62 @@ fn set_mask<R: Read>(tar: &mut Archive<R>) {
1046994 #[ cfg( unix) ]
1047995 tar. set_mask ( crate :: util:: get_umask ( ) ) ;
1048996}
997+
998+ /// Unpack a tarball with zip bomb and overwrite protections.
999+ fn unpack ( gctx : & GlobalContext , tarball : & File , unpack_dir : & Path ) -> CargoResult < u64 > {
1000+ let mut tar = {
1001+ let size_limit = max_unpack_size ( gctx, tarball. metadata ( ) ?. len ( ) ) ;
1002+ let gz = GzDecoder :: new ( tarball) ;
1003+ let gz = LimitErrorReader :: new ( gz, size_limit) ;
1004+ let mut tar = Archive :: new ( gz) ;
1005+ set_mask ( & mut tar) ;
1006+ tar
1007+ } ;
1008+ let mut bytes_written = 0 ;
1009+ let prefix = unpack_dir. file_name ( ) . unwrap ( ) ;
1010+ let parent = unpack_dir. parent ( ) . unwrap ( ) ;
1011+ for entry in tar. entries ( ) ? {
1012+ let mut entry = entry. context ( "failed to iterate over archive" ) ?;
1013+ let entry_path = entry
1014+ . path ( )
1015+ . context ( "failed to read entry path" ) ?
1016+ . into_owned ( ) ;
1017+
1018+ // We're going to unpack this tarball into the global source
1019+ // directory, but we want to make sure that it doesn't accidentally
1020+ // (or maliciously) overwrite source code from other crates. Cargo
1021+ // itself should never generate a tarball that hits this error, and
1022+ // crates.io should also block uploads with these sorts of tarballs,
1023+ // but be extra sure by adding a check here as well.
1024+ if !entry_path. starts_with ( prefix) {
1025+ anyhow:: bail!(
1026+ "invalid tarball downloaded, contains \
1027+ a file at {:?} which isn't under {:?}",
1028+ entry_path,
1029+ prefix
1030+ )
1031+ }
1032+ // Prevent unpacking the lockfile from the crate itself.
1033+ if entry_path
1034+ . file_name ( )
1035+ . map_or ( false , |p| p == PACKAGE_SOURCE_LOCK )
1036+ {
1037+ continue ;
1038+ }
1039+ // Unpacking failed
1040+ bytes_written += entry. size ( ) ;
1041+ let mut result = entry. unpack_in ( parent) . map_err ( anyhow:: Error :: from) ;
1042+ if cfg ! ( windows) && restricted_names:: is_windows_reserved_path ( & entry_path) {
1043+ result = result. with_context ( || {
1044+ format ! (
1045+ "`{}` appears to contain a reserved Windows path, \
1046+ it cannot be extracted on Windows",
1047+ entry_path. display( )
1048+ )
1049+ } ) ;
1050+ }
1051+ result. with_context ( || format ! ( "failed to unpack entry at `{}`" , entry_path. display( ) ) ) ?;
1052+ }
1053+
1054+ Ok ( bytes_written)
1055+ }
0 commit comments