From 640b179f4e7e86a0b71cd44a4423eabf356cde4c Mon Sep 17 00:00:00 2001 From: Martin Matuska Date: Fri, 2 Dec 2016 09:26:51 +0000 Subject: [PATCH] Update vendor/libarchive to git 53d73345410d69e68171f05facaf4523e38e72bb Vendor bugfixes: Fix for heap-buffer-overflow in archive_le16dec() Fix for heap-buffer-overflow in uudecode_bidder_bid() Reworked fix for compatibility with archives created by Perl Archive::Tar --- libarchive/archive_read_support_filter_uu.c | 5 +- libarchive/archive_read_support_format_cab.c | 5 +- libarchive/archive_read_support_format_tar.c | 109 ++++++++----------- 3 files changed, 51 insertions(+), 68 deletions(-) diff --git a/libarchive/archive_read_support_filter_uu.c b/libarchive/archive_read_support_filter_uu.c index 787a619f2f3a..f0fc14870123 100644 --- a/libarchive/archive_read_support_filter_uu.c +++ b/libarchive/archive_read_support_filter_uu.c @@ -312,6 +312,7 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *self, avail -= len; if (l == 6) { + /* "begin " */ if (!uuchar[*b]) return (0); /* Get a length of decoded bytes. */ @@ -352,8 +353,8 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *self, b += nl; if (avail && uuchar[*b]) return (firstline+30); - } - if (l == 13) { + } else if (l == 13) { + /* "begin-base64 " */ while (len-nl > 0) { if (!base64[*b++]) return (0); diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c index fc70684afa04..099f4a83dcac 100644 --- a/libarchive/archive_read_support_format_cab.c +++ b/libarchive/archive_read_support_format_cab.c @@ -645,12 +645,13 @@ cab_read_header(struct archive_read *a) cab = (struct cab *)(a->format->data); if (cab->found_header == 0 && p[0] == 'M' && p[1] == 'Z') { - /* This is an executable? Must be self-extracting... */ + /* This is an executable? Must be self-extracting... */ err = cab_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); - if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL) + /* Re-read header after processing the SFX. */ + if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); } diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index b977cb7400fb..071d766b74b7 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -297,57 +297,49 @@ archive_read_format_tar_cleanup(struct archive_read *a) /* * Validate number field * - * Flags: - * 1 - allow double \0 at field end + * This has to be pretty lenient in order to accomodate the enormous + * variety of tar writers in the world: + * = POSIX ustar requires octal values with leading zeros and + * specific termination on fields + * = Many writers use different termination (in particular, libarchive + * omits terminator bytes to squeeze one or two more digits) + * = Many writers pad with space and omit leading zeros + * = GNU tar and star write base-256 values if numbers are too + * big to be represented in octal + * + * This should tolerate all variants in use. It will reject a field + * where the writer just left garbage after a trailing NUL. */ static int -validate_number_field(const char* p_field, size_t i_size, int flags) +validate_number_field(const char* p_field, size_t i_size) { unsigned char marker = (unsigned char)p_field[0]; - /* octal? */ - if ((marker >= '0' && marker <= '7') || marker == ' ') { - size_t i = 0; - int octal_found = 0; - for (i = 0; i < i_size; ++i) { - switch (p_field[i]) - { - case ' ': - /* skip any leading spaces and trailing space */ - if (octal_found == 0 || i == i_size - 1) { - continue; - } - break; - case '\0': - /* - * null should be allowed only at the end - * - * Perl Archive::Tar terminates some fields - * with two nulls. We must allow this to stay - * compatible. - */ - if (i != i_size - 1) { - if (((flags & 1) == 0) - || i != i_size - 2) - return 0; - } - break; - /* rest must be octal digits */ - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - ++octal_found; - break; - } - } - return octal_found > 0; - } - /* base 256 (i.e. binary number) */ - else if (marker == 128 || marker == 255 || marker == 0) { - /* nothing to check */ + if (marker == 128 || marker == 255 || marker == 0) { + /* Base-256 marker, there's nothing we can check. */ + return 1; + } else { + /* Must be octal */ + size_t i = 0; + /* Skip any leading spaces */ + while (i < i_size && p_field[i] == ' ') { + ++i; + } + /* Must be at least one octal digit. */ + if (i >= i_size || p_field[i] < '0' || p_field[i] > '7') { + return 0; + } + /* Skip remaining octal digits. */ + while (i < i_size && p_field[i] >= '0' && p_field[i] <= '7') { + ++i; + } + /* Any remaining characters must be space or NUL padding. */ + while (i < i_size) { + if (p_field[i] != ' ' && p_field[i] != 0) { + return 0; + } + ++i; + } return 1; - } - /* not a number field */ - else { - return 0; } } @@ -404,26 +396,15 @@ archive_read_format_tar_bid(struct archive_read *a, int best_bid) /* * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields. - * These are usually octal numbers but GNU tar encodes "big" values as - * base256 and leading zeroes are sometimes replaced by spaces. - * Even the null terminator is sometimes omitted. Anyway, must be - * checked to avoid false positives. - * - * Perl Archive::Tar does not follow the spec and terminates mode, uid, - * gid, rdevmajor and rdevminor with a double \0. For compatibility - * reasons we allow this deviation. */ if (bid > 0 && ( - validate_number_field(header->mode, sizeof(header->mode), 1) == 0 - || validate_number_field(header->uid, sizeof(header->uid), 1) == 0 - || validate_number_field(header->gid, sizeof(header->gid), 1) == 0 - || validate_number_field(header->mtime, sizeof(header->mtime), - 0) == 0 - || validate_number_field(header->size, sizeof(header->size), 0) == 0 - || validate_number_field(header->rdevmajor, - sizeof(header->rdevmajor), 1) == 0 - || validate_number_field(header->rdevminor, - sizeof(header->rdevminor), 1) == 0)) { + validate_number_field(header->mode, sizeof(header->mode)) == 0 + || validate_number_field(header->uid, sizeof(header->uid)) == 0 + || validate_number_field(header->gid, sizeof(header->gid)) == 0 + || validate_number_field(header->mtime, sizeof(header->mtime)) == 0 + || validate_number_field(header->size, sizeof(header->size)) == 0 + || validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0 + || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) { bid = 0; }