>From 81e76e61df1a9bec4e1423f9f561394657cc1b93 Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: Wed, 9 Jul 2014 14:04:17 +0200 Subject: [PATCH] create: do not segfault with --dereference Fix SIGSEGV during archiving of directory symbolic-link loop. Prior to version 1.26, tar failed with message "Cannot stat: Too many levels of symbolic links" but this limit does not "save" us anymore since the s/open/openat/ conversion. Original bugreport: https://bugzilla.redhat.com/show_bug.cgi?id=1115890 * src/create.c (dir_loop_point): New function detecting loops in directory path when --dereference is specified. (dump_dir): Don't recurse down when dir_loop_point alerts. * tests/deref01.at: New testcase. * tests/Makefile.am: Adjust for new testcase. * tests/testsuite.at: Likewise. * NEWS: Document. --- NEWS | 5 +++++ src/create.c | 32 +++++++++++++++++++++++++++++++- tests/Makefile.am | 1 + tests/deref01.at | 41 +++++++++++++++++++++++++++++++++++++++++ tests/testsuite.at | 1 + 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/deref01.at diff --git a/NEWS b/NEWS index 3f63ed7..086cbaf 100644 --- a/NEWS +++ b/NEWS @@ -64,6 +64,11 @@ speed up archivation. * Tar refuses to read input from and write output to a tty device. +* Bug fixes + +Fix segfault during archiving of directory symbolic-link loop with +--dereference option. + * Manpages This release includes official tar(1) and rmt(8) manpages. diff --git a/src/create.c b/src/create.c index e2f4ede..ab03e19 100644 --- a/src/create.c +++ b/src/create.c @@ -1293,13 +1293,43 @@ get_directory_entries (struct tar_stat_info *st) return streamsavedir (st->dirstream, savedir_sort_order); } +static bool +dir_loop_point (const struct tar_stat_info* st) +{ + const struct tar_stat_info *ptr = st; + + if (!dereference_option) + return false; + + while (ptr->parent) + { + ptr = ptr->parent; + if (ptr->stat.st_dev == st->stat.st_dev + && ptr->stat.st_ino == st->stat.st_ino) + return true; + } + + return false; +} + /* Dump the directory ST. Return true if successful, false (emitting diagnostics) otherwise. Get ST's entries, recurse through its subdirectories, and clean up file descriptors afterwards. */ static bool dump_dir (struct tar_stat_info *st) { - char *directory = get_directory_entries (st); + char *directory; + + /* Do not follow already occuring items */ + if (dir_loop_point (st)) + { + WARN ((0, 0, _("%s: stopping recursion due to directory loop"), + st->orig_file_name)); + dump_dir0 (st, ""); + return true; + } + + directory = get_directory_entries (st); if (! directory) { savedir_diag (st->orig_file_name); diff --git a/tests/Makefile.am b/tests/Makefile.am index fd2def5..73d9c34 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -64,6 +64,7 @@ TESTSUITE_AT = \ delete03.at\ delete04.at\ delete05.at\ + deref01.at\ exclude.at\ exclude01.at\ exclude02.at\ diff --git a/tests/deref01.at b/tests/deref01.at new file mode 100644 index 0000000..30c499c --- /dev/null +++ b/tests/deref01.at @@ -0,0 +1,41 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# +# Test suite for GNU tar. +# Copyright 2014 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 . +# +# Test description: +# Symlink loop on directory with --dereference option caused tar to end in +# infinite recursion ending on SIGSEGV. +# +# Original bugreport: +# https://bugzilla.redhat.com/show_bug.cgi?id=1115890 + +AT_SETUP([dereference: symlink loop]) +AT_KEYWORDS([create dereference deref01]) + +AT_TAR_CHECK([ +mkdir dir +ln -s ../dir dir/dir || AT_SKIP_TEST +tar -chf test.tar dir 2>/dev/null && tar -tf test.tar +], +[0], +[dir/ +dir/dir/ +]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 7f8e4c4..0c3f2c2 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -214,6 +214,7 @@ m4_include([recurse.at]) m4_include([recurs02.at]) m4_include([shortrec.at]) m4_include([iotty.at]) +m4_include([deref01.at]) AT_BANNER([The --same-order option]) m4_include([same-order01.at]) -- 1.9.3