summaryrefslogtreecommitdiffstats
path: root/meta/recipes-extended/cpio/cpio-2.12/CVE-2019-14866.patch
blob: 5d587fc8326eb631ed1ecfff13c95cab2391b8ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
CVE: CVE-2019-14866
Upstream-Status: Backport [https://git.savannah.gnu.org/cgit/cpio.git/commit/?id=7554e3e42cd72f6f8304410c47fe6f8918e9bfd7]
Signed-off-by: Anuj Mittal <anuj.mittal@intel.com>

From a052401293e45a13cded5959b258204dae6d0af5 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org>
Date: Sun, 3 Nov 2019 23:59:39 +0200
Subject: [PATCH] Fix CVE-2019-14866

* src/copyout.c (to_ascii): Additional argument nul controls whether
to add the terminating nul character.
(field_width_error): Improve diagnostics: print the actual and the
maximum allowed field value.
* src/extern.h (to_ascii, field_width_error): New prototypes.
* src/tar.c (to_oct): Remove.
(to_oct_or_error): New function.
(TO_OCT): New macro.
(write_out_tar_header): Use TO_OCT and to_ascii. Return 0 on
success, 1 on error.
---
 src/copyout.c | 49 ++++++++++++++++++++++--------------
 src/extern.h  | 15 +++++++++--
 src/tar.c     | 69 ++++++++++++++++++++++++---------------------------
 3 files changed, 75 insertions(+), 58 deletions(-)

diff --git a/src/copyout.c b/src/copyout.c
index 1f0987a..1ae5477 100644
--- a/src/copyout.c
+++ b/src/copyout.c
@@ -269,26 +269,32 @@ writeout_final_defers (int out_des)
    so it should be moved to paxutils too.
    Allowed values for logbase are: 1 (binary), 2, 3 (octal), 4 (hex) */
 int
-to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase)
+to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase, bool nul)
 {
   static char codetab[] = "0123456789ABCDEF";
-  int i = digits;
-  
-  do
+
+  if (nul)
+    where[--digits] = 0;
+  while (digits > 0)
     {
-      where[--i] = codetab[(v & ((1 << logbase) - 1))];
+      where[--digits] = codetab[(v & ((1 << logbase) - 1))];
       v >>= logbase;
     }
-  while (i);
 
   return v != 0;
 }
 
-static void
-field_width_error (const char *filename, const char *fieldname)
+void
+field_width_error (const char *filename, const char *fieldname,
+		   uintmax_t value, size_t width, bool nul)
 {
-  error (0, 0, _("%s: field width not sufficient for storing %s"),
-	 filename, fieldname);
+  char valbuf[UINTMAX_STRSIZE_BOUND + 1];
+  char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
+  error (0, 0, _("%s: value %s %s out of allowed range 0..%s"),
+	 filename, fieldname,
+	 STRINGIFY_BIGINT (value, valbuf),
+	 STRINGIFY_BIGINT (MAX_VAL_WITH_DIGITS (width - nul, LG_8),
+			   maxbuf));
 }
 
 static void
@@ -303,7 +309,7 @@ to_ascii_or_warn (char *where, uintmax_t n, size_t digits,
 		  unsigned logbase,
 		  const char *filename, const char *fieldname)
 {
-  if (to_ascii (where, n, digits, logbase))
+  if (to_ascii (where, n, digits, logbase, false))
     field_width_warning (filename, fieldname);
 }    
 
@@ -312,9 +318,9 @@ to_ascii_or_error (char *where, uintmax_t n, size_t digits,
 		   unsigned logbase,
 		   const char *filename, const char *fieldname)
 {
-  if (to_ascii (where, n, digits, logbase))
+  if (to_ascii (where, n, digits, logbase, false))
     {
-      field_width_error (filename, fieldname);
+      field_width_error (filename, fieldname, n, digits, false);
       return 1;
     }
   return 0;
@@ -371,7 +377,7 @@ write_out_new_ascii_header (const char *magic_string,
 			 _("name size")))
     return 1;
   p += 8;
-  to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16);
+  to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16, false);
 
   tape_buffered_write (ascii_header, out_des, sizeof ascii_header);
 
@@ -388,7 +394,7 @@ write_out_old_ascii_header (dev_t dev, dev_t rdev,
   char ascii_header[76];
   char *p = ascii_header;
   
-  to_ascii (p, file_hdr->c_magic, 6, LG_8);
+  to_ascii (p, file_hdr->c_magic, 6, LG_8, false);
   p += 6;
   to_ascii_or_warn (p, dev, 6, LG_8, file_hdr->c_name, _("device number"));
   p += 6;
@@ -492,7 +498,10 @@ write_out_binary_header (dev_t rdev,
   short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF;
   if (short_hdr.c_namesize != file_hdr->c_namesize)
     {
-      field_width_error (file_hdr->c_name, _("name size"));
+      char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
+      error (0, 0, _("%s: value %s %s out of allowed range 0..%u"),
+	     file_hdr->c_name, _("name size"),
+	     STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFu);
       return 1;
     }
 		      
@@ -502,7 +511,10 @@ write_out_binary_header (dev_t rdev,
   if (((off_t)short_hdr.c_filesizes[0] << 16) + short_hdr.c_filesizes[1]
        != file_hdr->c_filesize)
     {
-      field_width_error (file_hdr->c_name, _("file size"));
+      char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
+      error (0, 0, _("%s: value %s %s out of allowed range 0..%lu"),
+	     file_hdr->c_name, _("file size"),
+	     STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFFFFFlu);
       return 1;
     }
 		      
@@ -552,8 +564,7 @@ write_out_header (struct cpio_file_stat *file_hdr, int out_des)
 	  error (0, 0, _("%s: file name too long"), file_hdr->c_name);
 	  return 1;
 	}
-      write_out_tar_header (file_hdr, out_des); /* FIXME: No error checking */
-      return 0;
+      return write_out_tar_header (file_hdr, out_des);
 
     case arf_binary:
       return write_out_binary_header (makedev (file_hdr->c_rdev_maj,
diff --git a/src/extern.h b/src/extern.h
index e27d662..f9ef56a 100644
--- a/src/extern.h
+++ b/src/extern.h
@@ -117,6 +117,10 @@ void print_name_with_quoting (char *p);
 /* copyout.c */
 int write_out_header (struct cpio_file_stat *file_hdr, int out_des);
 void process_copy_out (void);
+int to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase,
+	      bool nul);
+void field_width_error (const char *filename, const char *fieldname,
+			uintmax_t value, size_t width, bool nul);
 
 /* copypass.c */
 void process_copy_pass (void);
@@ -145,7 +149,7 @@ int make_path (char *argpath, uid_t owner, gid_t group,
 	       const char *verbose_fmt_string);
 
 /* tar.c */
-void write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des);
+int write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des);
 int null_block (long *block, int size);
 void read_in_tar_header (struct cpio_file_stat *file_hdr, int in_des);
 int otoa (char *s, unsigned long *n);
@@ -204,9 +208,16 @@ void cpio_safer_name_suffix (char *name, bool link_target,
 int cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir);
 void change_dir (void);
 
-/* FIXME: These two defines should be defined in paxutils */
+/* FIXME: The following three should be defined in paxutils */
 #define LG_8  3
 #define LG_16 4
+/* The maximum uintmax_t value that can be represented with DIGITS digits,
+   assuming that each digit is BITS_PER_DIGIT wide.  */
+#define MAX_VAL_WITH_DIGITS(digits, bits_per_digit) \
+   ((digits) * (bits_per_digit) < sizeof (uintmax_t) * CHAR_BIT \
+    ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \
+    : (uintmax_t) -1)
+
 
 uintmax_t from_ascii (char const *where, size_t digs, unsigned logbase);
 
diff --git a/src/tar.c b/src/tar.c
index a2ce171..ef58027 100644
--- a/src/tar.c
+++ b/src/tar.c
@@ -79,36 +79,17 @@ stash_tar_filename (char *prefix, char *filename)
   return hold_tar_filename;
 }
 
-/* Convert a number into a string of octal digits.
-   Convert long VALUE into a DIGITS-digit field at WHERE,
-   including a trailing space and room for a NUL.  DIGITS==3 means
-   1 digit, a space, and room for a NUL.
-
-   We assume the trailing NUL is already there and don't fill it in.
-   This fact is used by start_header and finish_header, so don't change it!
-
-   This is be equivalent to:
-   sprintf (where, "%*lo ", digits - 2, value);
-   except that sprintf fills in the trailing NUL and we don't.  */
-
-static void
-to_oct (register long value, register int digits, register char *where)
+static int
+to_oct_or_error (uintmax_t value, size_t digits, char *where, char const *field,
+		 char const *file)
 {
-  --digits;			/* Leave the trailing NUL slot alone.  */
-
-  /* Produce the digits -- at least one.  */
-  do
+  if (to_ascii (where, value, digits, LG_8, true))
     {
-      where[--digits] = '0' + (char) (value & 7); /* One octal digit.  */
-      value >>= 3;
+      field_width_error (file, field, value, digits, true);
+      return 1;
     }
-  while (digits > 0 && value != 0);
-
-  /* Add leading zeroes, if necessary.  */
-  while (digits > 0)
-    where[--digits] = '0';
+  return 0;
 }
-
 
 
 /* Compute and return a checksum for TAR_HDR,
@@ -134,10 +115,22 @@ tar_checksum (struct tar_header *tar_hdr)
   return sum;
 }
 
+#define TO_OCT(file_hdr, c_fld, digits, tar_hdr, tar_field) \
+  do							    \
+    {							    \
+       if (to_oct_or_error (file_hdr -> c_fld,		    \
+			    digits,			    \
+			    tar_hdr -> tar_field,	    \
+			    #tar_field,			    \
+			    file_hdr->c_name))		    \
+	 return 1;					    \
+    }							    \
+  while (0)
+
 /* Write out header FILE_HDR, including the file name, to file
    descriptor OUT_DES.  */
 
-void
+int
 write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
 {
   int name_len;
@@ -166,11 +159,11 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
 
   /* Ustar standard (POSIX.1-1988) requires the mode to contain only 3 octal
      digits */
-  to_oct (file_hdr->c_mode & MODE_ALL, 8, tar_hdr->mode);
-  to_oct (file_hdr->c_uid, 8, tar_hdr->uid);
-  to_oct (file_hdr->c_gid, 8, tar_hdr->gid);
-  to_oct (file_hdr->c_filesize, 12, tar_hdr->size);
-  to_oct (file_hdr->c_mtime, 12, tar_hdr->mtime);
+  TO_OCT (file_hdr, c_mode & MODE_ALL, 8, tar_hdr, mode);
+  TO_OCT (file_hdr, c_uid, 8, tar_hdr, uid);
+  TO_OCT (file_hdr, c_gid, 8, tar_hdr, gid);
+  TO_OCT (file_hdr, c_filesize, 12, tar_hdr, size);
+  TO_OCT (file_hdr, c_mtime, 12, tar_hdr, mtime);
 
   switch (file_hdr->c_mode & CP_IFMT)
     {
@@ -182,7 +175,7 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
 	  strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname,
 		   TARLINKNAMESIZE);
 	  tar_hdr->typeflag = LNKTYPE;
-	  to_oct (0, 12, tar_hdr->size);
+	  to_ascii (tar_hdr->size, 0, 12, LG_8, true);
 	}
       else
 	tar_hdr->typeflag = REGTYPE;
@@ -208,7 +201,7 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
 	 than TARLINKNAMESIZE.  */
       strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname,
 	       TARLINKNAMESIZE);
-      to_oct (0, 12, tar_hdr->size);
+      to_ascii (tar_hdr->size, 0, 12, LG_8, true);
       break;
 #endif /* CP_IFLNK */
     }
@@ -227,13 +220,15 @@ write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des)
       if (name)
 	strcpy (tar_hdr->gname, name);
 
-      to_oct (file_hdr->c_rdev_maj, 8, tar_hdr->devmajor);
-      to_oct (file_hdr->c_rdev_min, 8, tar_hdr->devminor);
+      TO_OCT (file_hdr, c_rdev_maj, 8, tar_hdr, devmajor);
+      TO_OCT (file_hdr, c_rdev_min, 8, tar_hdr, devminor);
     }
 
-  to_oct (tar_checksum (tar_hdr), 8, tar_hdr->chksum);
+  to_ascii (tar_hdr->chksum, tar_checksum (tar_hdr), 8, LG_8, true);
 
   tape_buffered_write ((char *) &tar_rec, out_des, TARRECORDSIZE);
+
+  return 0;
 }
 
 /* Return nonzero iff all the bytes in BLOCK are NUL.
-- 
2.24.1