Blame SOURCES/cpio-2.11-CVE-2019-14866.patch

d30f2d
From 0d5aa7b6578422d3a3a7cad315807ae5b53566b2 Mon Sep 17 00:00:00 2001
d30f2d
From: Ondrej Dubaj <odubaj@redhat.com>
d30f2d
Date: Thu, 19 Mar 2020 08:58:32 +0100
d30f2d
Subject: [PATCH] * src/copyout.c (to_ascii): Additional argument nul controls
d30f2d
 whether to add the terminating nul character. (field_width_error): Improve
d30f2d
 diagnostics: print the actual and the maximum allowed field value. *
d30f2d
 src/extern.h (to_ascii, field_width_error): New prototypes. * src/tar.c
d30f2d
 (to_oct): Remove. (to_oct_or_error): New function. (TO_OCT): New macro.
d30f2d
 (write_out_tar_header)
d30f2d
d30f2d
---
d30f2d
 src/copyout.c | 47 +++++++++++++++++++++-------------
d30f2d
 src/extern.h  | 15 +++++++++--
d30f2d
 src/tar.c     | 71 ++++++++++++++++++++++++---------------------------
d30f2d
 3 files changed, 75 insertions(+), 58 deletions(-)
d30f2d
d30f2d
diff --git a/src/copyout.c b/src/copyout.c
d30f2d
index 03eaf88..c0b1ffa 100644
d30f2d
--- a/src/copyout.c
d30f2d
+++ b/src/copyout.c
d30f2d
@@ -279,26 +279,32 @@ writeout_final_defers (int out_des)
d30f2d
    so it should be moved to paxutils too.
d30f2d
    Allowed values for logbase are: 1 (binary), 2, 3 (octal), 4 (hex) */
d30f2d
 int
d30f2d
-to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase)
d30f2d
+to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase, bool nul)
d30f2d
 {
d30f2d
   static char codetab[] = "0123456789ABCDEF";
d30f2d
-  int i = digits;
d30f2d
   
d30f2d
-  do
d30f2d
+  if (nul)
d30f2d
+    where[--digits] = 0;
d30f2d
+  while (digits > 0)
d30f2d
     {
d30f2d
-      where[--i] = codetab[(v & ((1 << logbase) - 1))];
d30f2d
+      where[--digits] = codetab[(v & ((1 << logbase) - 1))];
d30f2d
       v >>= logbase;
d30f2d
     }
d30f2d
-  while (i);
d30f2d
 
d30f2d
   return v != 0;
d30f2d
 }
d30f2d
 
d30f2d
-static void
d30f2d
-field_width_error (const char *filename, const char *fieldname)
d30f2d
+void
d30f2d
+field_width_error (const char *filename, const char *fieldname,
d30f2d
+		   uintmax_t value, size_t width, bool nul)
d30f2d
 {
d30f2d
-  error (1, 0, _("%s: field width not sufficient for storing %s"),
d30f2d
-	 filename, fieldname);
d30f2d
+  char valbuf[UINTMAX_STRSIZE_BOUND + 1];
d30f2d
+  char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
d30f2d
+  error (0, 0, _("%s: value %s %s out of allowed range 0..%s"),
d30f2d
+	 filename, fieldname,
d30f2d
+	 STRINGIFY_BIGINT (value, valbuf),
d30f2d
+	 STRINGIFY_BIGINT (MAX_VAL_WITH_DIGITS (width - nul, LG_8),
d30f2d
+			   maxbuf));
d30f2d
 }
d30f2d
 
d30f2d
 static void
d30f2d
@@ -313,7 +319,7 @@ to_ascii_or_warn (char *where, uintmax_t n, size_t digits,
d30f2d
 		  unsigned logbase,
d30f2d
 		  const char *filename, const char *fieldname)
d30f2d
 {
d30f2d
-  if (to_ascii (where, n, digits, logbase))
d30f2d
+  if (to_ascii (where, n, digits, logbase, false))
d30f2d
     field_width_warning (filename, fieldname);
d30f2d
 }    
d30f2d
 
d30f2d
@@ -322,9 +328,9 @@ to_ascii_or_error (char *where, uintmax_t n, size_t digits,
d30f2d
 		   unsigned logbase,
d30f2d
 		   const char *filename, const char *fieldname)
d30f2d
 {
d30f2d
-  if (to_ascii (where, n, digits, logbase))
d30f2d
+  if (to_ascii (where, n, digits, logbase, false))
d30f2d
     {
d30f2d
-      field_width_error (filename, fieldname);
d30f2d
+      field_width_error (filename, fieldname, n, digits, false);
d30f2d
       return 1;
d30f2d
     }
d30f2d
   return 0;
d30f2d
@@ -381,7 +387,7 @@ write_out_new_ascii_header (const char *magic_string,
d30f2d
 			 _("name size")))
d30f2d
     return 1;
d30f2d
   p += 8;
d30f2d
-  to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16);
d30f2d
+  to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16, false);
d30f2d
 
d30f2d
   tape_buffered_write (ascii_header, out_des, sizeof ascii_header);
d30f2d
 
d30f2d
@@ -398,7 +404,7 @@ write_out_old_ascii_header (dev_t dev, dev_t rdev,
d30f2d
   char ascii_header[76];
d30f2d
   char *p = ascii_header;
d30f2d
   
d30f2d
-  to_ascii (p, file_hdr->c_magic, 6, LG_8);
d30f2d
+  to_ascii (p, file_hdr->c_magic, 6, LG_8, false);
d30f2d
   p += 6;
d30f2d
   to_ascii_or_warn (p, dev, 6, LG_8, file_hdr->c_name, _("device number"));
d30f2d
   p += 6;
d30f2d
@@ -502,7 +508,10 @@ write_out_binary_header (dev_t rdev,
d30f2d
   short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF;
d30f2d
   if (short_hdr.c_namesize != file_hdr->c_namesize)
d30f2d
     {
d30f2d
-      field_width_error (file_hdr->c_name, _("name size"));
d30f2d
+      char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
d30f2d
+      error (0, 0, _("%s: value %s %s out of allowed range 0..%u"),
d30f2d
+	     file_hdr->c_name, _("name size"),
d30f2d
+	     STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFu);
d30f2d
       return 1;
d30f2d
     }
d30f2d
 		      
d30f2d
@@ -512,7 +521,10 @@ write_out_binary_header (dev_t rdev,
d30f2d
   if (((off_t)short_hdr.c_filesizes[0] << 16) + short_hdr.c_filesizes[1]
d30f2d
        != file_hdr->c_filesize)
d30f2d
     {
d30f2d
-      field_width_error (file_hdr->c_name, _("file size"));
d30f2d
+      char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
d30f2d
+      error (0, 0, _("%s: value %s %s out of allowed range 0..%lu"),
d30f2d
+	     file_hdr->c_name, _("file size"),
d30f2d
+	     STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFFFFFlu);
d30f2d
       return 1;
d30f2d
     }
d30f2d
 		      
d30f2d
@@ -562,8 +574,7 @@ write_out_header (struct cpio_file_stat *file_hdr, int out_des)
d30f2d
 	  error (0, 0, _("%s: file name too long"), file_hdr->c_name);
d30f2d
 	  return 1;
d30f2d
 	}
d30f2d
-      write_out_tar_header (file_hdr, out_des); /* FIXME: No error checking */
d30f2d
-      return 0;
d30f2d
+      return write_out_tar_header (file_hdr, out_des);
d30f2d
 
d30f2d
     case arf_binary:
d30f2d
       return write_out_binary_header (makedev (file_hdr->c_rdev_maj,
d30f2d
diff --git a/src/extern.h b/src/extern.h
d30f2d
index 1c6f70b..a0b6009 100644
d30f2d
--- a/src/extern.h
d30f2d
+++ b/src/extern.h
d30f2d
@@ -117,6 +117,10 @@ void print_name_with_quoting (char *p);
d30f2d
 /* copyout.c */
d30f2d
 int write_out_header (struct cpio_file_stat *file_hdr, int out_des);
d30f2d
 void process_copy_out (void);
d30f2d
+int to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase,
d30f2d
+	      bool nul);
d30f2d
+void field_width_error (const char *filename, const char *fieldname,
d30f2d
+			uintmax_t value, size_t width, bool nul);
d30f2d
 
d30f2d
 /* copypass.c */
d30f2d
 void process_copy_pass (void);
d30f2d
@@ -145,7 +149,7 @@ int make_path (char *argpath, uid_t owner, gid_t group,
d30f2d
 	       const char *verbose_fmt_string);
d30f2d
 
d30f2d
 /* tar.c */
d30f2d
-void write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des);
d30f2d
+int write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des);
d30f2d
 int null_block (long *block, int size);
d30f2d
 void read_in_tar_header (struct cpio_file_stat *file_hdr, int in_des);
d30f2d
 int otoa (char *s, unsigned long *n);
d30f2d
@@ -203,9 +207,16 @@ void cpio_safer_name_suffix (char *name, bool link_target,
d30f2d
 			     bool absolute_names, bool strip_leading_dots);
d30f2d
 int cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir);
d30f2d
 
d30f2d
-/* FIXME: These two defines should be defined in paxutils */
d30f2d
+/* FIXME: The following three should be defined in paxutils */
d30f2d
 #define LG_8  3
d30f2d
 #define LG_16 4
d30f2d
+/* The maximum uintmax_t value that can be represented with DIGITS digits,
d30f2d
+   assuming that each digit is BITS_PER_DIGIT wide.  */
d30f2d
+#define MAX_VAL_WITH_DIGITS(digits, bits_per_digit) \
d30f2d
+   ((digits) * (bits_per_digit) < sizeof (uintmax_t) * CHAR_BIT \
d30f2d
+    ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \
d30f2d
+    : (uintmax_t) -1)
d30f2d
+
d30f2d
 
d30f2d
 uintmax_t from_ascii (char const *where, size_t digs, unsigned logbase);
d30f2d
 
d30f2d
diff --git a/src/tar.c b/src/tar.c
d30f2d
index 854878e..fe8fdcf 100644
d30f2d
--- a/src/tar.c
d30f2d
+++ b/src/tar.c
d30f2d
@@ -1,6 +1,5 @@
d30f2d
 /* tar.c - read in write tar headers for cpio
d30f2d
-   Copyright (C) 1992, 2001, 2004, 2006, 2007, 2010 Free Software
d30f2d
-   Foundation, Inc.
d30f2d
+   Copyright (C) 1992-2019 Free Software Foundation, Inc.
d30f2d
 
d30f2d
    This program is free software; you can redistribute it and/or modify
d30f2d
    it under the terms of the GNU General Public License as published by
d30f2d
@@ -81,34 +80,16 @@ stash_tar_filename (char *prefix, char *filename)
d30f2d
   return hold_tar_filename;
d30f2d
 }
d30f2d
 
d30f2d
-/* Convert a number into a string of octal digits.
d30f2d
-   Convert long VALUE into a DIGITS-digit field at WHERE,
d30f2d
-   including a trailing space and room for a NUL.  DIGITS==3 means
d30f2d
-   1 digit, a space, and room for a NUL.
d30f2d
-
d30f2d
-   We assume the trailing NUL is already there and don't fill it in.
d30f2d
-   This fact is used by start_header and finish_header, so don't change it!
d30f2d
-
d30f2d
-   This is be equivalent to:
d30f2d
-   sprintf (where, "%*lo ", digits - 2, value);
d30f2d
-   except that sprintf fills in the trailing NUL and we don't.  */
d30f2d
-
d30f2d
-static void
d30f2d
-to_oct (register long value, register int digits, register char *where)
d30f2d
+static int
d30f2d
+to_oct_or_error (uintmax_t value, size_t digits, char *where, char const *field,
d30f2d
+		 char const *file)
d30f2d
 {
d30f2d
-  --digits;			/* Leave the trailing NUL slot alone.  */
d30f2d
-
d30f2d
-  /* Produce the digits -- at least one.  */
d30f2d
-  do
d30f2d
+  if (to_ascii (where, value, digits, LG_8, true))
d30f2d
     {
d30f2d
-      where[--digits] = '0' + (char) (value & 7); /* One octal digit.  */
d30f2d
-      value >>= 3;
d30f2d
+      field_width_error (file, field, value, digits, true);
d30f2d
+      return 1;
d30f2d
     }
d30f2d
-  while (digits > 0 && value != 0);
d30f2d
-
d30f2d
-  /* Add leading zeroes, if necessary.  */
d30f2d
-  while (digits > 0)
d30f2d
-    where[--digits] = '0';
d30f2d
+  return 0;
d30f2d
 }
d30f2d
 
d30f2d
 
d30f2d
@@ -136,10 +117,22 @@ tar_checksum (struct tar_header *tar_hdr)
d30f2d
   return sum;
d30f2d
 }
d30f2d
 
d30f2d
+#define TO_OCT(file_hdr, c_fld, digits, tar_hdr, tar_field) \
d30f2d
+  do							    \
d30f2d
+    {							    \
d30f2d
+       if (to_oct_or_error (file_hdr -> c_fld,		    \
d30f2d
+			    digits,			    \
d30f2d
+			    tar_hdr -> tar_field,	    \
d30f2d
+			    #tar_field,			    \
d30f2d
+			    file_hdr->c_name))		    \
d30f2d
+	 return 1;					    \
d30f2d
+    }							    \
d30f2d
+  while (0)
d30f2d
+
d30f2d
 /* Write out header FILE_HDR, including the file name, to file
d30f2d
    descriptor OUT_DES.  */
d30f2d
 
d30f2d
-void
d30f2d
+int
d30f2d
 write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
d30f2d
 {
d30f2d
   int name_len;
d30f2d
@@ -168,11 +161,11 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
d30f2d
 
d30f2d
   /* Ustar standard (POSIX.1-1988) requires the mode to contain only 3 octal
d30f2d
      digits */
d30f2d
-  to_oct (file_hdr->c_mode & MODE_ALL, 8, tar_hdr->mode);
d30f2d
-  to_oct (file_hdr->c_uid, 8, tar_hdr->uid);
d30f2d
-  to_oct (file_hdr->c_gid, 8, tar_hdr->gid);
d30f2d
-  to_oct (file_hdr->c_filesize, 12, tar_hdr->size);
d30f2d
-  to_oct (file_hdr->c_mtime, 12, tar_hdr->mtime);
d30f2d
+  TO_OCT (file_hdr, c_mode & MODE_ALL, 8, tar_hdr, mode);
d30f2d
+  TO_OCT (file_hdr, c_uid, 8, tar_hdr, uid);
d30f2d
+  TO_OCT (file_hdr, c_gid, 8, tar_hdr, gid);
d30f2d
+  TO_OCT (file_hdr, c_filesize, 12, tar_hdr, size);
d30f2d
+  TO_OCT (file_hdr, c_mtime, 12, tar_hdr, mtime);
d30f2d
 
d30f2d
   switch (file_hdr->c_mode & CP_IFMT)
d30f2d
     {
d30f2d
@@ -184,7 +177,7 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
d30f2d
 	  strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname,
d30f2d
 		   TARLINKNAMESIZE);
d30f2d
 	  tar_hdr->typeflag = LNKTYPE;
d30f2d
-	  to_oct (0, 12, tar_hdr->size);
d30f2d
+	  to_ascii (tar_hdr->size, 0, 12, LG_8, true);
d30f2d
 	}
d30f2d
       else
d30f2d
 	tar_hdr->typeflag = REGTYPE;
d30f2d
@@ -210,7 +203,7 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
d30f2d
 	 than TARLINKNAMESIZE.  */
d30f2d
       strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname,
d30f2d
 	       TARLINKNAMESIZE);
d30f2d
-      to_oct (0, 12, tar_hdr->size);
d30f2d
+      to_ascii (tar_hdr->size, 0, 12, LG_8, true);
d30f2d
       break;
d30f2d
 #endif /* CP_IFLNK */
d30f2d
     }
d30f2d
@@ -229,13 +222,15 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
d30f2d
       if (name)
d30f2d
 	strcpy (tar_hdr->gname, name);
d30f2d
 
d30f2d
-      to_oct (file_hdr->c_rdev_maj, 8, tar_hdr->devmajor);
d30f2d
-      to_oct (file_hdr->c_rdev_min, 8, tar_hdr->devminor);
d30f2d
+      TO_OCT (file_hdr, c_rdev_maj, 8, tar_hdr, devmajor);
d30f2d
+      TO_OCT (file_hdr, c_rdev_min, 8, tar_hdr, devminor);
d30f2d
     }
d30f2d
 
d30f2d
-  to_oct (tar_checksum (tar_hdr), 8, tar_hdr->chksum);
d30f2d
+  to_ascii (tar_hdr->chksum, tar_checksum (tar_hdr), 8, LG_8, true);
d30f2d
 
d30f2d
   tape_buffered_write ((char *) &tar_rec, out_des, TARRECORDSIZE);
d30f2d
+
d30f2d
+  return 0;
d30f2d
 }
d30f2d
 
d30f2d
 /* Return nonzero iff all the bytes in BLOCK are NUL.
d30f2d
-- 
d30f2d
2.24.1
d30f2d