On Saturday 22 of November 2014 23:37:07 Michal Zalewski wrote:
> Please see this test case for cpio 2.11:
> 
> http://lcamtuf.coredump.cx/afl/vulns/lesspipe-cpio-bad-write.cpio

Possible fix attached.

Pavel
>From dd3c5e3d4b44155807a6466385b98401aaca0f21 Mon Sep 17 00:00:00 2001
From: Pavel Raiskup <prais...@redhat.com>
Date: Tue, 25 Nov 2014 13:21:55 +0100
Subject: [PATCH] copyin: buffer-alloc the link name

References:
http://lists.gnu.org/archive/html/bug-cpio/2014-11/msg00007.html

* gnulib.modules: Include 'obstack' module.
* src/copyin.c: Define obstack_chunk_alloc and obstack_chunk_free
to be used by <obstack.h> include.
(read_link_name_from_file, get_link_name): New local functions
which use buffering for link name reading.
(list_file): Use get_link_name().
(copyin_link): Use get_link_name().
* tests/symlink-bad-length.at: New testcase.
* tests/symlink-long.at: Likewise.
* tests/Makefile.am: Mention new test-cases.
* tests/testsuite.at: Likewise.
---
 gnulib.modules              |   1 +
 src/copyin.c                | 104 +++++++++++++++++++++++++++-----------------
 tests/Makefile.am           |   2 +
 tests/symlink-bad-length.at |  48 ++++++++++++++++++++
 tests/symlink-long.at       |  46 ++++++++++++++++++++
 tests/testsuite.at          |   2 +
 6 files changed, 162 insertions(+), 41 deletions(-)
 create mode 100644 tests/symlink-bad-length.at
 create mode 100644 tests/symlink-long.at

diff --git a/gnulib.modules b/gnulib.modules
index f165870..4c99bc0 100644
--- a/gnulib.modules
+++ b/gnulib.modules
@@ -17,6 +17,7 @@ gitlog-to-changelog
 hash
 inttypes
 lchown
+obstack
 progname
 safe-read
 savedir
diff --git a/src/copyin.c b/src/copyin.c
index 38d809f..faa3dca 100644
--- a/src/copyin.c
+++ b/src/copyin.c
@@ -32,6 +32,10 @@
 # include <fnmatch.h>
 #endif
 
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+#include <obstack.h>
+
 #ifndef HAVE_LCHOWN
 # define lchown(f,u,g) 0
 #endif
@@ -125,36 +129,66 @@ tape_skip_padding (int in_file_des, off_t offset)
     tape_toss_input (in_file_des, pad);
 }
 
+#ifdef CP_IFLNK
+static char *
+read_link_name_from_file (struct cpio_file_stat *file_hdr,
+                          int in_file_des)
+{
+#define READBUFFSIZE 512
+  char buff[READBUFFSIZE];
+  off_t chunk, remains = file_hdr->c_filesize;
+  char *link_name = 0;
+  struct obstack stk;
+
+  obstack_init (&stk);
+
+  while (remains > 0)
+    {
+      chunk = remains < READBUFFSIZE ? remains : READBUFFSIZE;
+      tape_buffered_read (buff, in_file_des, chunk);
+      obstack_grow (&stk, buff, chunk);
+      remains -= chunk;
+    }
+
+  obstack_1grow (&stk, '\0');
+  link_name = xstrdup (obstack_finish (&stk));
+
+  obstack_free (&stk, NULL);
+  tape_skip_padding (in_file_des, file_hdr->c_filesize);
+
+  return link_name;
+#undef READBUFFSIZE
+}
+#endif /* CP_IFLNK */
+
+static char *
+get_link_name (struct cpio_file_stat* file_hdr, int in_file_des)
+{
+  char *link_name = NULL;
+
+#ifdef CP_IFLNK
+  if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
+    {
+      if (archive_format != arf_tar && archive_format != arf_ustar)
+        link_name = read_link_name_from_file (file_hdr, in_file_des);
+      else
+        link_name = xstrdup (file_hdr->c_tar_linkname);
+    }
+#endif
+
+  return link_name;
+}
+
 
 static void
 list_file(struct cpio_file_stat* file_hdr, int in_file_des)
 {
   if (verbose_flag)
     {
-#ifdef CP_IFLNK
-      if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
-	{
-	  if (archive_format != arf_tar && archive_format != arf_ustar)
-	    {
-	      char *link_name = NULL;	/* Name of hard and symbolic links.  */
-
-	      link_name = (char *) xmalloc ((unsigned int) file_hdr->c_filesize + 1);
-	      link_name[file_hdr->c_filesize] = '\0';
-	      tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize);
-	      long_format (file_hdr, link_name);
-	      free (link_name);
-	      tape_skip_padding (in_file_des, file_hdr->c_filesize);
-	      return;
-	    }
-	  else
-	    {
-	      long_format (file_hdr, file_hdr->c_tar_linkname);
-	      return;
-	    }
-	}
-      else
-#endif
-	long_format (file_hdr, (char *) 0);
+      char *linkname = get_link_name (file_hdr, in_file_des);
+      long_format (file_hdr, linkname);
+      free (linkname);
+      return;
     }
   else
     {
@@ -635,24 +669,12 @@ copyin_link(struct cpio_file_stat *file_hdr, int in_file_des)
   char *link_name = NULL;	/* Name of hard and symbolic links.  */
   int res;			/* Result of various function calls.  */
 
-  if (archive_format != arf_tar && archive_format != arf_ustar)
+  /* make sure get_link_name to make sure the buffer is moved correctly */
+  link_name = get_link_name (file_hdr, in_file_des);
+  if (to_stdout_option)
     {
-      if (to_stdout_option)
-        {
-          tape_toss_input (in_file_des, file_hdr->c_filesize);
-          tape_skip_padding (in_file_des, file_hdr->c_filesize);
-          return;
-        }
-      link_name = (char *) xmalloc ((unsigned int) file_hdr->c_filesize + 1);
-      link_name[file_hdr->c_filesize] = '\0';
-      tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize);
-      tape_skip_padding (in_file_des, file_hdr->c_filesize);
-    }
-  else
-    {
-      if (to_stdout_option)
-        return;
-      link_name = xstrdup (file_hdr->c_tar_linkname);
+      free (link_name);
+      return;
     }
 
   res = UMASKED_SYMLINK (link_name, file_hdr->c_name,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3f714d1..b4ca92d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -51,6 +51,8 @@ TESTSUITE_AT = \
  setstat04.at\
  setstat05.at\
  symlink.at\
+ symlink-bad-length.at\
+ symlink-long.at\
  symlink-to-stdout.at\
  version.at
 
diff --git a/tests/symlink-bad-length.at b/tests/symlink-bad-length.at
new file mode 100644
index 0000000..88677a3
--- /dev/null
+++ b/tests/symlink-bad-length.at
@@ -0,0 +1,48 @@
+# Process this file with autom4te to create testsuite.  -*- Autotest -*-
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# This program 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, or (at your option)
+# any later version.
+
+# This program 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA.
+
+# Cpio v2.11 did segfault with badly set symlink length.
+# References:
+# http://lists.gnu.org/archive/html/bug-cpio/2014-11/msg00007.html
+
+AT_SETUP([symlink-bad-length])
+AT_KEYWORDS([symlink-long copyout])
+
+AT_DATA([ARCHIVE_base64],
+[x3EjAIBAtIEtJy8nAQAAAHRUYW0FAAAADQBGSUxFAABzb21lIGNvbnRlbnQKAMdxIwBgQ/+hLScv
+JwEAAAB0VEhuBQD/////TElOSwAARklMRcdxAAAAAAAAAAAAAAEAAAAAAAAACwAAAAAAVFJBSUxF
+UiEhIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+])
+
+AT_CHECK([
+base64 -d ARCHIVE_base64 > ARCHIVE || AT_SKIP_TEST
+cpio -tv < ARCHIVE
+test $? -eq 2
+],
+[0],
+[-rw-rw-r--   1 cpio_use cpio_use       13 Nov 25 12:52 FILE
+],[cpio: premature end of file
+])
+
+AT_CLEANUP
diff --git a/tests/symlink-long.at b/tests/symlink-long.at
new file mode 100644
index 0000000..d3def2d
--- /dev/null
+++ b/tests/symlink-long.at
@@ -0,0 +1,46 @@
+# Process this file with autom4te to create testsuite.  -*- Autotest -*-
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# This program 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, or (at your option)
+# any later version.
+
+# This program 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA.
+
+# Cpio v2.11.90 changed the way symlink name is read from archive.
+# References:
+# http://lists.gnu.org/archive/html/bug-cpio/2014-11/msg00007.html
+
+AT_SETUP([symlink-long])
+AT_KEYWORDS([symlink-long copyout])
+
+AT_CHECK([
+
+# len(dirname) > READBUFSIZE
+dirname=
+for i in {1..52}; do
+    dirname="xxxxxxxxx/$dirname"
+    mkdir "$dirname"
+done
+ln -s "$dirname" x || AT_SKIP_TEST
+
+echo x | cpio -o > ar
+list=`cpio -tv < ar | sed 's|.*-> ||'`
+test "$list" = "$dirname" && echo success || echo fail
+],
+[0],
+[success
+],[2 blocks
+2 blocks
+])
+
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index e67689f..3b5377e 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -32,6 +32,8 @@ m4_include([version.at])
 
 m4_include([inout.at])
 m4_include([symlink.at])
+m4_include([symlink-bad-length.at])
+m4_include([symlink-long.at])
 m4_include([symlink-to-stdout.at])
 m4_include([interdir.at])
 
-- 
1.9.3

Reply via email to