Subject says it pretty much all, but see the Red Hat bugreport for
backtrace and other files [1].

Attached patch fixes this by not recursing down to the loop - not sure
whether the looping directory point should be backed up also, but
it makes sense IMO (as the symlink itself would be otherwise backed up if
the --dereference option was not specified.

[1] https://bugzilla.redhat.com/show_bug.cgi?id=1115890

Pavel
>From 81e76e61df1a9bec4e1423f9f561394657cc1b93 Mon Sep 17 00:00:00 2001
From: Pavel Raiskup <[email protected]>
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 <http://www.gnu.org/licenses/>.
+#
+# 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

Reply via email to