cpio/SOURCES/cpio-2.12-improper-input-va...

155 lines
5.5 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From: Thomas Habets <habets@google.com>
Subject: [PATCH] Check for size overflow in tar header fields.
This prevents surprising outputs being created, e.g. this cpio tar
output with more than one file:
tar cf suffix.tar AUTHORS
dd if=/dev/zero seek=16G bs=1 count=0 of=suffix.tar
echo suffix.tar | cpio -H tar -o | tar tvf -
-rw-r--r-- 1000/1000 0 2019-08-30 16:40 suffix.tar
-rw-r--r-- thomas/thomas 161 2019-08-30 16:40 AUTHORS
---
src/copyout.c | 3 +--
src/extern.h | 2 +-
src/tar.c | 45 ++++++++++++++++++++++++++++++++-------------
3 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/src/copyout.c b/src/copyout.c
index dcae449..56416ba 100644
--- a/src/copyout.c
+++ b/src/copyout.c
@@ -552,8 +552,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..47b477a 100644
--- a/src/extern.h
+++ b/src/extern.h
@@ -145,7 +145,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);
diff --git a/src/tar.c b/src/tar.c
index e2b5f45..53dc99a 100644
--- a/src/tar.c
+++ b/src/tar.c
@@ -93,8 +93,9 @@ stash_tar_filename (char *prefix, char *filename)
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 (register long value, register int digits, register char *where,
+ const char *filename, const char *fieldname)
{
--digits; /* Leave the trailing NUL slot alone. */
@@ -105,10 +106,17 @@ to_oct (register long value, register int digits, register char *where)
value >>= 3;
}
while (digits > 0 && value != 0);
+ if (value > 0)
+ {
+ error (0, 0, _("%s: field width not sufficient for storing %s"),
+ filename, fieldname);
+ return 1;
+ }
/* Add leading zeroes, if necessary. */
while (digits > 0)
where[--digits] = '0';
+ return 0;
}
@@ -139,7 +147,7 @@ tar_checksum (struct tar_header *tar_hdr)
/* 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;
@@ -168,11 +176,16 @@ 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);
+ if (to_oct_or_error (file_hdr->c_mode & MODE_ALL, 8, tar_hdr->mode, file_hdr->c_name, _("mode")))
+ return 1;
+ if (to_oct_or_error (file_hdr->c_uid, 8, tar_hdr->uid, file_hdr->c_name, _("uid")))
+ return 1;
+ if (to_oct_or_error (file_hdr->c_gid, 8, tar_hdr->gid, file_hdr->c_name, _("gid")))
+ return 1;
+ if (to_oct_or_error (file_hdr->c_filesize, 12, tar_hdr->size, file_hdr->c_name, _("file size")))
+ return 1;
+ if (to_oct_or_error (file_hdr->c_mtime, 12, tar_hdr->mtime, file_hdr->c_name, _("modification time")))
+ return 1;
switch (file_hdr->c_mode & CP_IFMT)
{
@@ -184,7 +197,8 @@ 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);
+ if (to_oct_or_error (0, 12, tar_hdr->size, file_hdr->c_name, _("file size")))
+ return 1;
}
else
tar_hdr->typeflag = REGTYPE;
@@ -210,7 +224,8 @@ 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);
+ if (to_oct_or_error (0, 12, tar_hdr->size, file_hdr->c_name, _("file size")))
+ return 1;
break;
#endif /* CP_IFLNK */
}
@@ -229,13 +244,17 @@ 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);
+ if (to_oct_or_error (file_hdr->c_rdev_maj, 8, tar_hdr->devmajor, file_hdr->c_name, _("rdev major")))
+ return 1;
+ if (to_oct_or_error (file_hdr->c_rdev_min, 8, tar_hdr->devminor, file_hdr->c_name, _("rdev minor")))
+ return 1;
}
- to_oct (tar_checksum (tar_hdr), 8, tar_hdr->chksum);
+ if (to_oct_or_error (tar_checksum (tar_hdr), 8, tar_hdr->chksum, file_hdr->c_name, _("checksum")))
+ return 1;
tape_buffered_write ((char *) &tar_rec, out_des, TARRECORDSIZE);
+ return 0;
}
/* Return nonzero iff all the bytes in BLOCK are NUL.
--
2.26.0