From: Paul Eggert Date: Mon, 13 Jul 2015 16:53:00 +0000 (-0700) Subject: tar: fix symlink race and symlink transform bug X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Ftar;a=commitdiff_plain;h=fe3b106cb3b0bc4d50230ed263bf5ccbc8bf1ea7 tar: fix symlink race and symlink transform bug Problem reported by Tobias Stoeckmann in: http://lists.gnu.org/archive/html/bug-tar/2015-07/msg00004.html * gnulib.modules: Add areadlinkat-with-size. * src/create.c: Include areadlink.h. (dump_file0): Use areadlinkat_with_size, rather than trying to do it by hand, incorrectly. This also avoids assumption that the symlink contents fit on the stack. Also, use the transformed link name, not the original link name, when deciding whether the name is long enough to require writing a long link. --- diff --git a/gnulib.modules b/gnulib.modules index ec3dc90..fe5ab73 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -19,6 +19,7 @@ # along with this program. If not, see . alloca +areadlinkat-with-size argmatch argp argp-version-etc diff --git a/src/create.c b/src/create.c index 1b08e0b..7cdc978 100644 --- a/src/create.c +++ b/src/create.c @@ -22,6 +22,7 @@ #include +#include #include #include "common.h" @@ -1114,7 +1115,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory) return; info_attach_exclist (st); - + if (incremental_option && archive_format != POSIX_FORMAT) blk->header.typeflag = GNUTYPE_DUMPDIR; else /* if (standard_option) */ @@ -1198,7 +1199,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory) char const *entry; size_t entry_len; size_t name_len; - + name_buf = xstrdup (st->orig_file_name); name_size = name_len = strlen (name_buf); @@ -1837,22 +1838,17 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p) #ifdef HAVE_READLINK else if (S_ISLNK (st->stat.st_mode)) { - char *buffer; - int size; - size_t linklen = st->stat.st_size; - if (linklen != st->stat.st_size || linklen + 1 == 0) - xalloc_die (); - buffer = (char *) alloca (linklen + 1); - size = readlinkat (parentfd, name, buffer, linklen + 1); - if (size < 0) + st->link_name = areadlinkat_with_size (parentfd, name, st->stat.st_size); + if (!st->link_name) { + if (errno == ENOMEM) + xalloc_die (); file_removed_diag (p, top_level, readlink_diag); return; } - buffer[size] = '\0'; - assign_string (&st->link_name, buffer); transform_name (&st->link_name, XFORM_SYMLINK); - if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size) + if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) + < strlen (st->link_name)) write_long_link (st); xattrs_selinux_get (parentfd, name, st, 0);