From 746f3ff670dcfcdd28fcc990e79cd6fccc7ae48d Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Mon, 01 Dec 2014 13:15:28 +0000
Subject: Fix memory overrun on reading improperly created link records.

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

* src/copyin.c (get_link_name): New function.
(list_file, copyin_link): use get_link_name

* tests/symlink-bad-length.at: New file.
* tests/symlink-long.at: New file.
* tests/Makefile.am: Add new files.
* tests/testsuite.at: Likewise.

From 54d1c42ac2cb91389fca04a5018ad573e4ae265a Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Mon, 01 Dec 2014 19:10:39 +0000
Subject: Bugfix

* src/copyin.c (get_link_name): Fix range checking.
* tests/symlink-bad-length.at: Change expected error message.

From 58df4f1b44a1142bba500f980fd26806413b1728 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Tue, 02 Dec 2014 09:33:29 +0000
Subject: Fix typo

From fd262d116c4564c1796be9be2799619cf7785d07 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Thu, 11 Dec 2014 10:51:21 +0000
Subject: Fix error recovery in copy-in mode

* src/copyin.c (copyin_link): Fix null dereference.
(read_in_header): Fix error recovery (bug introduced by
27e0ae55).
* tests/symlink-bad-length.at: Test error recovery.
Catch various architecture-dependent error messages (suggested
by Pavel Raiskup).

From f6a8a2cbd2d5ca40ea94900b55b845dd5ca87328 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org.ua>
Date: Thu, 11 Dec 2014 13:21:40 +0000
Subject: Fix symlink-bad-length test for 64-bit architectures.

* src/util.c: Return non-zero exit code if EOF is hit prematurely.
* tests/symlink-bad-length.at: Revert to original archive: there's
no use testing for recovery, because that depends on the host
architecture.  Don't test for exit code as well (same reason).
Account for eventual warning messages.

(All the above git commits are related to CVE-2014-9112 --sbeattie)
---
 src/copyin.c                |   60 +++++++++++++++++++++++++----------------
 src/util.c                  |    5 ---
 tests/Makefile.am           |    2 +
 tests/symlink-bad-length.at |   64 ++++++++++++++++++++++++++++++++++++++++++++
 tests/symlink-long.at       |   46 +++++++++++++++++++++++++++++++
 tests/testsuite.at          |    2 +
 6 files changed, 152 insertions(+), 27 deletions(-)

Index: b/src/copyin.c
===================================================================
--- a/src/copyin.c
+++ b/src/copyin.c
@@ -124,10 +124,30 @@ tape_skip_padding (int in_file_des, off_
   if (pad != 0)
     tape_toss_input (in_file_des, pad);
 }
-
+
+static char *
+get_link_name (struct cpio_file_stat *file_hdr, int in_file_des)
+{
+  char *link_name;
+  
+  if (file_hdr->c_filesize < 0 || file_hdr->c_filesize > SIZE_MAX-1)
+    {
+      error (0, 0, _("%s: stored filename length is out of range"),
+	     file_hdr->c_name);
+      link_name = NULL;
+    }
+  else
+    {
+      link_name = xmalloc (file_hdr->c_filesize + 1);
+      tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize);
+      link_name[file_hdr->c_filesize] = '\0';
+      tape_skip_padding (in_file_des, file_hdr->c_filesize);
+    }
+  return link_name;
+}
 
 static void
-list_file(struct cpio_file_stat* file_hdr, int in_file_des)
+list_file (struct cpio_file_stat* file_hdr, int in_file_des)
 {
   if (verbose_flag)
     {
@@ -136,21 +156,16 @@ list_file(struct cpio_file_stat* file_hd
 	{
 	  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;
+	      char *link_name = get_link_name (file_hdr, in_file_des);
+	      if (link_name)
+		{
+		  long_format (file_hdr, link_name);
+		  free (link_name);
+		}
 	    }
 	  else
-	    {
-	      long_format (file_hdr, file_hdr->c_tar_linkname);
-	      return;
-	    }
+	    long_format (file_hdr, file_hdr->c_tar_linkname);
+	  return;
 	}
       else
 #endif
@@ -640,7 +655,7 @@ copyin_device (struct cpio_file_stat* fi
 }
 
 static void
-copyin_link(struct cpio_file_stat *file_hdr, int in_file_des)
+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.  */
@@ -650,10 +665,9 @@ copyin_link(struct cpio_file_stat *file_
 
   if (archive_format != arf_tar && archive_format != arf_ustar)
     {
-      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);
+      link_name = get_link_name (file_hdr, in_file_des);
+      if (!link_name)
+	return;
     }
   else
     {
@@ -1005,7 +1019,7 @@ read_in_header (struct cpio_file_stat *f
 
   file_hdr->c_tar_linkname = NULL;
 
-  tape_buffered_read (magic.str, in_des, 6L);
+  tape_buffered_read (magic.str, in_des, sizeof (magic.str));
   while (1)
     {
       if (append_flag)
@@ -1050,8 +1064,8 @@ read_in_header (struct cpio_file_stat *f
 	  break;
 	}
       bytes_skipped++;
-      memmove (magic.str, magic.str + 1, 5);
-      tape_buffered_read (magic.str, in_des, 1L);
+      memmove (magic.str, magic.str + 1, sizeof (magic.str) - 1);
+      tape_buffered_read (magic.str + sizeof (magic.str) - 1, in_des, 1L);
     }
 }
 
Index: b/tests/Makefile.am
===================================================================
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -52,6 +52,8 @@ TESTSUITE_AT = \
  setstat04.at\
  setstat05.at\
  symlink.at\
+ symlink-bad-length.at\
+ symlink-long.at\
  version.at
 
 TESTSUITE = $(srcdir)/testsuite
Index: b/tests/symlink-bad-length.at
===================================================================
--- /dev/null
+++ b/tests/symlink-bad-length.at
@@ -0,0 +1,64 @@
+# 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=
+])
+
+# The exact error message and exit status depend on the host architecture,
+# therefore strderr is filtered out and error code is not checked.
+
+# So far the only case when cpio would exit with code 0 is when it skips
+# several bytes and encounters a valid record header.  Perhaps it should
+# exit with code 2 (non-critical error), if at least one byte was skipped,
+# but that could hurt backward compatibility.
+
+AT_CHECK([
+base64 -d ARCHIVE.base64 > ARCHIVE || AT_SKIP_TEST
+TZ=UTC cpio -ntv < ARCHIVE 2>stderr 
+cat stderr | grep -v \
+    -e 'stored filename length is out of range' \
+    -e 'premature end of file' \
+    -e 'archive header has reverse byte-order' \
+    -e 'memory exhausted' \
+    -e 'skipped [[0-9][0-9]*] bytes of junk' \
+    -e '[[0-9][0-9]*] block' \
+    >&2
+echo >&2 STDERR
+],
+[0],
+[-rw-rw-r--   1 10029    10031          13 Nov 25 11:52 FILE
+],[STDERR
+])
+
+AT_CLEANUP
Index: b/tests/symlink-long.at
===================================================================
--- /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
Index: b/tests/testsuite.at
===================================================================
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -31,6 +31,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([interdir.at])
 
 m4_include([setstat01.at])
Index: b/src/util.c
===================================================================
--- a/src/util.c
+++ b/src/util.c
@@ -206,10 +206,7 @@ tape_fill_input_buffer (int in_des, int
   if (input_size < 0)
     error (1, errno, _("read error"));
   if (input_size == 0)
-    {
-      error (0, 0, _("premature end of file"));
-      exit (1);
-    }
+    error (PAXEXIT_FAILURE, 0, _("premature end of file"));
   input_bytes += input_size;
 }