diff --git a/THANKS b/THANKS index 20afca38..f908c1b0 100644 --- a/THANKS +++ b/THANKS @@ -6,6 +6,7 @@ Many people further contributed to GNU tar by reporting problems, suggesting various improvements or submitting actual code. Here is a list of these people. Help me keep it complete and exempt of errors. See various ChangeLogs for a detailed description of contributions. +This listed is sorted via "LC_ALL=C sort". Aage Robeck aagero@ifi.uio.no Adam Borowski kilobyte@angband.pl @@ -36,8 +37,8 @@ Andrew J. Schorr schorr@ead.dsa.com Andrew Torda torda@igc.chem.ethz.ch Andrey A. Chernov ache@astral.msk.su Andy Gay andy@rdl.co.uk -Antonio Jose Coutinho ajc@di.uminho.pt Anthony G. Basile blueness@gentoo.org +Antonio Jose Coutinho ajc@di.uminho.pt Ariel Faigon ariel@engr.sgi.com Arne Wichmann aw@math.uni-sb.de Arnold Robbins arnold@gnu.org @@ -79,9 +80,9 @@ Cesar Romani romani@ifm.uni-hamburg.de Chad Hurwitz churritz@cts.com Chance Reschke creschke@usra.edu Charles Fu ccwf@klab.caltech.edu -Charles McGarvey chazmcgarvey@brokenzipper.com Charles Lopes Charles.Lopes@infm.ulst.ac.uk Charles M. Hannum mycroft@gnu.org +Charles McGarvey chazmcgarvey@brokenzipper.com Chip Salzenberg tct!chip Chris Arthur csa@gnu.org Chris F.M. Verberne verberne@prl.philips.nl @@ -93,9 +94,9 @@ Christian Callsen Christian.Callsen@eng.sun.com Christian Kirsch ck@held.mind.de Christian Laubscher christian.laubscher@tiscalinet.ch Christian T. Dum ctd@mpe-garching.mpg.de -Christian von Roques roques@pond.sub.org -Christian Wetzel wetzel@phoenix-pacs.de Christian Weisgerber naddy@mips.inka.de +Christian Wetzel wetzel@phoenix-pacs.de +Christian von Roques roques@pond.sub.org Christoph Litauer litauer@mailhost.uni-koblenz.de Christophe Colle colle@krtkg1.rug.ac.be Christophe Kalt Christophe.Kalt@kbcfp.com @@ -117,8 +118,8 @@ Dan Bloch dan@transarc.com Dan Drake dan@dandrake.org Dan Reish dreish@izzy.net Daniel Hagerty hag@gnu.org -Daniel Quinlan quinlan@pathname.com Daniel Kahn Gillmor dkg@fifthhorseman.net +Daniel Quinlan quinlan@pathname.com Daniel R. Guilderson d.guilderson@ma30.bull.com Daniel S. Barclay daniel@compass-da.com Daniel Trinkle trinkle@cs.purdue.edu @@ -198,6 +199,7 @@ Greg Hudson ghudson@mit.edu Greg Maples greg@clari.net Greg McGary gkm@cstone.net Greg Schafer gschafer@zip.com.au +Guillermo de Angel gdeangelg@gmail.com Göran Uddeborg gvran@uddeborg.pp.se Gürkan Karaman karaman@dssgmbh.de Hans Guerth 100664.3101@compuserve.com @@ -222,6 +224,7 @@ Indra Singhal indra@synoptics.com J. Dean Brock brock@cs.unca.edu J.J. Bailey jjb@jagware.bcc.com J.T. Conklin jtc@cygnus.com +James Antill jantill@redhat.com James Crawford Ralston qralston+@pitt.edu James E. Carpenter jimc@zach1.tiac.net James H Caldwell Jr caldwell@cs.fsu.edu @@ -233,15 +236,15 @@ Jan Carlson janc@sni.ca Jan Djarv jan.djarv@mbox200.swipnet.se Janice Burton r06a165@bcc25.kodak.com Janne Snabb snabb@niksula.hut.fi -Jason R. Mastaler jason@webmaster.net Jason Armistead Jason.Armistead@otis.com +Jason R. Mastaler jason@webmaster.net Jay Fenlason hack@gnu.org Jean-Louis Martineau martineau@zmanda.com -Jean-Michel Soenen soenen@lectra.fr Jean-Loup Gailly jloup@chorus.fr -Jeff Moskow jeff@rtr.com +Jean-Michel Soenen soenen@lectra.fr Jean-Ph. Martin-Flatin syj@ecmwf.int Jean-Pierre Demailly Jean-Pierre.Demailly@ujf-grenoble.fr +Jeff Moskow jeff@rtr.com Jeff Prothero jsp@betz.biostr.washington.edu Jeff Siegel js@hornet.att.com Jeff Sorensen sorenj@alumni.rpi.edu @@ -249,7 +252,6 @@ Jeffrey Goldberg J.Goldberg@cranfield.ac.uk Jeffrey Mark Siskind Qobi@emba.uvm.edu Jeffrey W. Parker jwpkr@mcs.com Jens Henrik Jensen recjhl@mediator.uni-c.dk -Jérémy Bobbio lunar@debian.org Jim Blandy jimb@totoro.cs.oberlin.edu Jim Clausing jac@postbox.acs.ohio-state.edu Jim Farrell jwf@platinum.com @@ -283,13 +285,14 @@ Joutsiniemi Tommi Il tj75064@cs.tut.fi Joy Kendall jak8@world.std.com Judy Ricker jricker@gdstech.grumman.com Juha Sarlin juha@tds.kth.se -Jürgen Botz jbotz@orixa.mtholyoke.edu Jyh-Shyang Wang erik@vsp.ee.nctu.edu.tw +Jérémy Bobbio lunar@debian.org Jörg Schilling schilling@fokus.fraunhofer.de -Jörg Weule weule@cs.uni-duesseldorf.de Jörg Weilbier gnu@weilbier.net +Jörg Weule weule@cs.uni-duesseldorf.de Jörgen Hågg Jorgen.Hagg@axis.se Jörgen Weigert jw@suse.de +Jürgen Botz jbotz@orixa.mtholyoke.edu Jürgen Lüters jlueters@t-online.de Jürgen Reiss reiss@psychologie.uni-wuerzburg.de Kai Petzke wpp@marie.physik.tu-berlin.de @@ -312,7 +315,6 @@ Kirill Furman kfurman@astralinux.ru Koji Kishi kis@rqa.sony.co.jp Konno Hiroharu konno@pac.co.jp Kurt Jaeger pi@lf.net -James Antill jantill@redhat.com Larry Creech lcreech@lonestar.rcclub.org Larry Schwimmer rosebud@cyclone.stanford.edu Lasse Collin lasse.collin@tukaani.org @@ -396,7 +398,6 @@ Oswald P. Backus IV backus@lks.csi.com Pascal Meheut pascal@cnam.cnam.fr Patrick Fulconis fulco@sig.uvsq.fr Patrick Timmons timmons@electech.polymtl.ca -Pavel Raiskup praiskup@redhat.com Paul Eggert eggert@cs.ucla.edu Paul Kanz paul@icx.com Paul Mitchell P.Mitchell@surrey.ac.uk @@ -404,6 +405,7 @@ Paul Nevai pali+@osu.edu Paul Nordstrom 100067.3532@compuserve.com Paul O'Connor oconnorp@ul.ie Paul Siddall pauls@postman.essex.ac.uk +Pavel Raiskup praiskup@redhat.com Peder Chr. Norgaard pcn@tbit.dk Pekka Janhunen Pekka.Janhunen@fmi.fi Per Bojsen pb@delta.dk @@ -422,9 +424,9 @@ Piotr Rotter piotr.rotter@active24.pl R. Kent Dybvig dyb@cadence.bloomington.in.us R. Scott Butler butler@prism.es.dupont.com Rainer Orth ro@TechFak.Uni-Bielefeld.DE -Ralf Wildenhues Ralf.Wildenhues@gmx.de Ralf S. Engelschall rse@engelschall.com Ralf Suckow suckow@contrib.de +Ralf Wildenhues Ralf.Wildenhues@gmx.de Ralph Corderoy ralph@inputplus.co.uk Ralph Schleicher rs@purple.ul.bawue.de Randy Bias randyb@edge.edge.net diff --git a/src/extract.c b/src/extract.c index ab83a650..f9623a45 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1132,7 +1132,7 @@ safe_dir_mode (struct stat const *st) /* Extractor functions for various member types */ static bool -extract_dir (char *file_name, char typeflag) +extract_dir (char *file_name, char UNNAMED (typeflag)) { int status; mode_t mode; @@ -1157,8 +1157,6 @@ extract_dir (char *file_name, char typeflag) if (incremental_option) /* Read the entry and delete files that aren't listed in the archive. */ purge_directory (file_name); - else if (typeflag == GNUTYPE_DUMPDIR) - skip_member (); mode = safe_dir_mode (¤t_stat_info.stat); @@ -1338,10 +1336,7 @@ extract_file (char *file_name, char typeflag) { fd = sys_exec_command (file_name, 'f', ¤t_stat_info); if (fd < 0) - { - skip_member (); - return true; - } + return true; } else { @@ -1362,7 +1357,6 @@ extract_file (char *file_name, char typeflag) = maybe_recoverable (file_name, true, &interdir_made); if (recover != RECOVER_OK) { - skip_member (); if (recover == RECOVER_SKIP) return true; open_error (file_name); @@ -1409,6 +1403,7 @@ extract_file (char *file_name, char typeflag) } skim_file (size, false); + current_stat_info.skipped = true; mv_end (); @@ -1921,15 +1916,9 @@ extract_archive (void) tar_extractor_t fun = prepare_to_extract (current_stat_info.file_name, typeflag); - if (fun) - { - if (fun (current_stat_info.file_name, typeflag)) - return; - } - else - skip_member (); - - if (backup_option) + bool ok = fun && fun (current_stat_info.file_name, typeflag); + skip_member (); + if (!ok && backup_option) undo_last_backup (); } diff --git a/src/incremen.c b/src/incremen.c index b7b0d1cc..6be07193 100644 --- a/src/incremen.c +++ b/src/incremen.c @@ -1621,8 +1621,8 @@ dumpdir_ok (char *dumpdir) /* Examine the directories under directory_name and delete any files that were not there at the time of the back-up. */ -static bool -try_purge_directory (char const *directory_name) +void +purge_directory (char const *directory_name) { char *current_dir; char *cur, *arc, *p; @@ -1630,18 +1630,18 @@ try_purge_directory (char const *directory_name) struct dumpdir *dump; if (!is_dumpdir (¤t_stat_info)) - return false; + return; current_dir = tar_savedir (directory_name, false); if (!current_dir) /* The directory doesn't exist now. It'll be created. In any case, we don't have to delete any files out of it. */ - return false; + return; /* Verify if dump directory is sane */ if (!dumpdir_ok (current_stat_info.dumpdir)) - return false; + return; /* Process renames */ for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1) @@ -1661,7 +1661,7 @@ try_purge_directory (char const *directory_name) quote (temp_stub)); free (temp_stub); free (current_dir); - return false; + return; } } else if (*arc == 'R') @@ -1695,7 +1695,7 @@ try_purge_directory (char const *directory_name) free (current_dir); /* FIXME: Make sure purge_directory(dst) will return immediately */ - return false; + return; } } } @@ -1749,14 +1749,6 @@ try_purge_directory (char const *directory_name) dumpdir_free (dump); free (current_dir); - return true; -} - -void -purge_directory (char const *directory_name) -{ - if (!try_purge_directory (directory_name)) - skip_member (); } void diff --git a/src/list.c b/src/list.c index d541cf26..8e9caf5c 100644 --- a/src/list.c +++ b/src/list.c @@ -423,20 +423,15 @@ read_header (union block **return_block, struct tar_stat_info *info, if ((status = tar_checksum (header, false)) != HEADER_SUCCESS) break; - /* Good block. Decode file size and return. */ - - if (header->header.typeflag == LNKTYPE) - info->stat.st_size = 0; /* links 0 size on tape */ - else + info->stat.st_size = OFF_FROM_HEADER (header->header.size); + if (info->stat.st_size < 0) { - info->stat.st_size = OFF_FROM_HEADER (header->header.size); - if (info->stat.st_size < 0) - { - status = HEADER_FAILURE; - break; - } + status = HEADER_FAILURE; + break; } + info->skipped = false; + if (header->header.typeflag == GNUTYPE_LONGNAME || header->header.typeflag == GNUTYPE_LONGLINK || header->header.typeflag == XHDTYPE @@ -493,11 +488,15 @@ read_header (union block **return_block, struct tar_stat_info *info, } *bp = '\0'; + info->skipped = true; } else if (header->header.typeflag == XHDTYPE || header->header.typeflag == SOLARIS_XHDTYPE) - xheader_read (&info->xhdr, header, - OFF_FROM_HEADER (header->header.size)); + { + xheader_read (&info->xhdr, header, + OFF_FROM_HEADER (header->header.size)); + info->skipped = true; + } else if (header->header.typeflag == XGLTYPE) { struct xheader xhdr; @@ -511,6 +510,7 @@ read_header (union block **return_block, struct tar_stat_info *info, OFF_FROM_HEADER (header->header.size)); xheader_decode_global (&xhdr); xheader_destroy (&xhdr); + info->skipped = true; if (mode == read_header_x_global) { status = HEADER_SUCCESS_EXTENDED; @@ -1412,23 +1412,6 @@ skip_member (void) skim_member (false); } -static bool -member_is_dir (struct tar_stat_info *info, char typeflag) -{ - switch (typeflag) { - case AREGTYPE: - case REGTYPE: - case CONTTYPE: - return info->had_trailing_slash; - - case DIRTYPE: - return true; - - default: - return false; - } -} - /* Skip the current member in the archive. If MUST_COPY, always copy instead of skipping. */ void @@ -1436,18 +1419,17 @@ skim_member (bool must_copy) { if (!current_stat_info.skipped) { - bool is_dir = member_is_dir (¤t_stat_info, - current_header->header.typeflag); set_next_block_after (current_header); mv_begin_read (¤t_stat_info); if (current_stat_info.is_sparse) sparse_skim_file (¤t_stat_info, must_copy); - else if (!is_dir) + else skim_file (current_stat_info.stat.st_size, must_copy); mv_end (); + current_stat_info.skipped = true; } } diff --git a/tests/Makefile.am b/tests/Makefile.am index ae8bd3ff..0625b021 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -141,6 +141,7 @@ TESTSUITE_AT = \ extrac29.at\ extrac30.at\ extrac31.at\ + extrac32.at\ filerem01.at\ filerem02.at\ filerem03.at\ diff --git a/tests/extrac32.at b/tests/extrac32.at new file mode 100644 index 00000000..3829a483 --- /dev/null +++ b/tests/extrac32.at @@ -0,0 +1,47 @@ +# Check for file injection bug with symlinks. -*- Autotest -*- + +# Copyright 2026 Free Software Foundation, Inc. + +# This file is part of GNU tar. + +# GNU tar is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# GNU tar is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Thanks to Guillermo de Angel for the bug report and test cases; see: +# https://lists.gnu.org/r/bug-tar/2026-03/msg00007.html + +AT_SETUP([skip file injection]) +AT_KEYWORDS([injection]) +AT_DATA([archive.in], +[/Td6WFoAAATm1rRGBMDbAYAcIQEcAAAAAAAAACYr+9LgDf8A010AMZhKvfVdtHe4Rxjj7M03ek97 +UgeKfJ0ORqYg0XDFntWxdTH4PYrTOo9CoqBrnTM2NcwFBrRVr7aFwdd56vddyAw2QGDjxgNexDU3 +ImTi/+z8ZOLMi/+AybdEpd5aA/M9Maa+8tQ84bySzSAwrmxMWJJ6W9IKvsqfiRa3TrD51v44PZU/ +KLVKpocS56n/O3g+b+hiZwaysR0eLO+tiU8FB/e3PEq3vTtDFVi/YfZMieBWSzomSX9eF13K1yPY +UuWgp7VokXqduL0YGNVV40MTPG9oAAAApD6mpajengIAAfcBgBwAAOM4xw6xxGf7AgAAAAAEWVo= +]) +AT_CHECK([base64 --help >/dev/null 2>&1 || AT_SKIP_TEST +xz --help >/dev/null 2>&1 || AT_SKIP_TEST +base64 -d < archive.in | xz -c -d > archive.tar +]) +cp archive.tar /tmp +AT_CHECK([tar tf archive.tar], +[0], +[carrier_entry +marker.txt +]) +AT_CHECK([tar xvf archive.tar], +[0], +[carrier_entry +marker.txt +]) +AT_CLEANUP diff --git a/tests/skipdir.at b/tests/skipdir.at index 0bc38e4f..79119ddf 100644 --- a/tests/skipdir.at +++ b/tests/skipdir.at @@ -42,15 +42,11 @@ base64 -d < archive.in | xz -c -d > archive.tar AT_CHECK([tar tf archive.tar], [0], [owo1/ -owo2/ ]) AT_CHECK([tar vxf archive.tar], [0], [owo1/ -owo2/ ]) AT_CHECK([tar -xvf archive.tar --exclude owo1], -[0], -[owo2/ -]) +[0]) AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 63458eae..5979512a 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -358,6 +358,7 @@ m4_include([extrac28.at]) m4_include([extrac29.at]) m4_include([extrac30.at]) m4_include([extrac31.at]) +m4_include([extrac32.at]) m4_include([backup01.at])