[klibc] cpio: Fix possible integer overflow on 32-bit systems
authorBen Hutchings <ben@decadent.org.uk>
Wed, 28 Apr 2021 03:16:34 +0000 (05:16 +0200)
committerBen Hutchings <benh@debian.org>
Sat, 5 Jun 2021 18:20:42 +0000 (19:20 +0100)
Origin: https://git.kernel.org/pub/scm/libs/klibc/klibc.git/commit/?id=9b1c91577aef7f2e72c3aa11a27749160bd278ff
Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2021-31872

The maximum name and file sizes in the "new" header format are 32-bit
unsigned values.  However, the I/O functions mostly use long for sizes
and offsets, so that sizes >= 2^31 are handled wrongly on 32-bit
systems.

The current GNU cpio code doesn't seem to have this problem, but the
divergence between this version and that is large enough that I can't
simply cherry-pick a fix for it.

As a short-term fix, in read_in_new_ascii(), fail if c_namesize or
c_filesize is > LONG_MAX.

CVE-2021-31872

Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Gbp-Pq: Name 0039-klibc-cpio-Fix-possible-integer-overflow-on-32-bit-s.patch

usr/utils/cpio.c

index cb616791c0aa4944dd571d6ff118ad72dd2864c5..ac481310bf982fc5881aba55bafccea3ba194971 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <malloc.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -904,6 +905,15 @@ static void read_in_new_ascii(struct new_cpio_header *file_hdr, int in_des)
                file_hdr->c_hdr[i] = strtoul(hexbuf, NULL, 16);
                ah += 8;
        }
+
+       /* Sizes > LONG_MAX can currently result in integer overflow
+          in various places.  Fail if name is too large. */
+       if (file_hdr->c_namesize > LONG_MAX) {
+               fprintf(stderr, "%s: name size out of range\n",
+                       progname);
+               exit(1);
+       }
+
        /* Read file name from input.  */
        free(file_hdr->c_name);
        file_hdr->c_name = (char *)xmalloc(file_hdr->c_namesize);
@@ -914,6 +924,14 @@ static void read_in_new_ascii(struct new_cpio_header *file_hdr, int in_des)
           is rounded up to the next long-word, so we might need to drop
           1-3 bytes.  */
        tape_skip_padding(in_des, file_hdr->c_namesize + 110);
+
+       /* Fail if file is too large.  We could check this earlier
+          but it's helpful to report the name. */
+       if (file_hdr->c_filesize > LONG_MAX) {
+               fprintf(stderr, "%s: %s: file size out of range\n",
+                       progname, file_hdr->c_name);
+               exit(1);
+       }
 }
 
 /* Return 16-bit integer I with the bytes swapped.  */