aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Hutchings <ben@decadent.org.uk>2021-04-28 05:16:34 +0200
committerBen Hutchings <ben@decadent.org.uk>2021-04-29 16:02:58 +0200
commit9b1c91577aef7f2e72c3aa11a27749160bd278ff (patch)
treee2abf1a1fb6eace0e786118b4a35fc25cb9b42de
parentabe5c3477ffa5e91029ef040aede622145dcc777 (diff)
downloadklibc-9b1c91577aef7f2e72c3aa11a27749160bd278ff.tar.gz
[klibc] cpio: Fix possible integer overflow on 32-bit systems
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>
-rw-r--r--usr/utils/cpio.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/usr/utils/cpio.c b/usr/utils/cpio.c
index cb616791c0aa4..ac481310bf982 100644
--- a/usr/utils/cpio.c
+++ b/usr/utils/cpio.c
@@ -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. */