diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28c8fa4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/libarchive-3.1.2.tar.gz diff --git a/.libarchive.metadata b/.libarchive.metadata new file mode 100644 index 0000000..158fc8e --- /dev/null +++ b/.libarchive.metadata @@ -0,0 +1 @@ +6a991777ecb0f890be931cec4aec856d1a195489 SOURCES/libarchive-3.1.2.tar.gz diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8916-CVE-2015-8917.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8916-CVE-2015-8917.patch new file mode 100644 index 0000000..9b6fc9e --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8916-CVE-2015-8917.patch @@ -0,0 +1,35 @@ +From b2e2abbb13ddcd962470cc1adb43b085f6e407a4 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Fri, 6 Feb 2015 22:45:58 -0800 +Subject: [PATCH] Issues 396, 397: Ignore entries with empty filenames. + +Bugs in the rar and cab readers lead to returning entries +with empty filenames. Make bsdtar resistant to this. + +Of course, we should also fix the rar and cab +readers to handle these cases correctly and either +return correctly-populated entries or fail cleanly. +--- + tar/read.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/tar/read.c b/tar/read.c +index 8267b70..430cff0 100644 +--- a/tar/read.c ++++ b/tar/read.c +@@ -264,6 +264,12 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) + } + if (r == ARCHIVE_FATAL) + break; ++ const char *p = archive_entry_pathname(entry); ++ if (p == NULL || p[0] == '\0') { ++ lafe_warnc(0, "Archive entry has empty or unreadable filename ... skipping."); ++ bsdtar->return_value = 1; ++ continue; ++ } + + if (bsdtar->uid >= 0) { + archive_entry_set_uid(entry, bsdtar->uid); +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8919.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8919.patch new file mode 100644 index 0000000..747a75d --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8919.patch @@ -0,0 +1,37 @@ +From e8a2e4d2e6b450a239bb8f9d74239fa434bf7d35 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 7 Feb 2015 13:32:58 -0800 +Subject: [PATCH] Issue 402: Failed to recognize empty dir name in lha/lzh file + +When parsing a directory name, we checked for the name +length being zero, but not for the first byte being a +null byte. Add a similar check for the file case. +--- + libarchive/archive_read_support_format_lha.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c +index 572686a..f8e01af 100644 +--- a/libarchive/archive_read_support_format_lha.c ++++ b/libarchive/archive_read_support_format_lha.c +@@ -1194,13 +1194,15 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, + archive_string_empty(&lha->filename); + break; + } ++ if (extdheader[0] == '\0') ++ goto invalid; + archive_strncpy(&lha->filename, + (const char *)extdheader, datasize); + break; + case EXT_DIRECTORY: +- if (datasize == 0) ++ if (datasize == 0 || extdheader[0] == '\0') + /* no directory name data. exit this case. */ +- break; ++ goto invalid; + + archive_strncpy(&lha->dirname, + (const char *)extdheader, datasize); +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8920.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8920.patch new file mode 100644 index 0000000..6bf2db3 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8920.patch @@ -0,0 +1,87 @@ +From 97f964e3e0ce3ae34bfb4c366a37ba7c0d9610a6 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 7 Feb 2015 12:35:33 -0800 +Subject: [PATCH] Issue 403: Buffer underflow parsing 'ar' header + +While pruning trailing text from ar filenames, we did not +check for an empty filename. This results in reading the byte +before the filename on the stack. + +While here, change a number of ar format issues from WARN to FATAL. +It's better to abort on a damaged file than risk reading garbage. +No doubt, this will require additional tuning in the future. +--- + libarchive/archive_read_support_format_ar.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c +index 82756c9..4b5b66b 100644 +--- a/libarchive/archive_read_support_format_ar.c ++++ b/libarchive/archive_read_support_format_ar.c +@@ -180,7 +180,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, + if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) { + archive_set_error(&a->archive, EINVAL, + "Incorrect file header signature"); +- return (ARCHIVE_WARN); ++ return (ARCHIVE_FATAL); + } + + /* Copy filename into work buffer. */ +@@ -239,8 +239,15 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, + * and are not terminated in '/', so we don't trim anything + * that starts with '/'.) + */ +- if (filename[0] != '/' && *p == '/') ++ if (filename[0] != '/' && p > filename && *p == '/') { + *p = '\0'; ++ } ++ ++ if (p < filename) { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, ++ "Found entry with empty filename"); ++ return (ARCHIVE_FATAL); ++ } + + /* + * '//' is the GNU filename table. +@@ -262,12 +269,12 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, + if (entry_size == 0) { + archive_set_error(&a->archive, EINVAL, + "Invalid string table"); +- return (ARCHIVE_WARN); ++ return (ARCHIVE_FATAL); + } + if (ar->strtab != NULL) { + archive_set_error(&a->archive, EINVAL, + "More than one string tables exist"); +- return (ARCHIVE_WARN); ++ return (ARCHIVE_FATAL); + } + + /* Read the filename table into memory. */ +@@ -311,11 +318,11 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, + */ + if (ar->strtab == NULL || number > ar->strtab_size) { + archive_set_error(&a->archive, EINVAL, +- "Can't find long filename for entry"); ++ "Can't find long filename for GNU/SVR4 archive entry"); + archive_entry_copy_pathname(entry, filename); + /* Parse the time, owner, mode, size fields. */ + ar_parse_common_header(ar, entry, h); +- return (ARCHIVE_WARN); ++ return (ARCHIVE_FATAL); + } + + archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]); +@@ -573,7 +580,7 @@ bad_string_table: + "Invalid string table"); + free(ar->strtab); + ar->strtab = NULL; +- return (ARCHIVE_WARN); ++ return (ARCHIVE_FATAL); + } + + static uint64_t +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8921.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8921.patch new file mode 100644 index 0000000..4d74549 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8921.patch @@ -0,0 +1,76 @@ +From 6c2128928b93902d4af154a954ffa383591feba4 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 7 Feb 2015 12:59:39 -0800 +Subject: [PATCH] Issue 404: Read past end of string parsing fflags + +-- + +Correct the spelling of 'wcscmp'. + +-- + +A correct fix for Issue 404: Read past end of string parsing fflags + +The previous fix actually broke the fflag parsing. We +cannot use strcmp() here because we're comparing a null-terminated +string to a part of another string. + +This fix explicitly tracks the various string lengths and +checks that they match before calling memcmp() or wmemcmp(). +That avoids any buffer overrun without breaking the parser. + +-- +Generated from commits: 1cbc76faff 05a875fdb8 90632371f +--- + libarchive/archive_entry.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c +index 7958a17..787d281 100644 +--- a/libarchive/archive_entry.c ++++ b/libarchive/archive_entry.c +@@ -1744,14 +1744,17 @@ ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) + while (*end != '\0' && *end != '\t' && + *end != ' ' && *end != ',') + end++; ++ size_t length = end - start; + for (flag = flags; flag->name != NULL; flag++) { +- if (memcmp(start, flag->name, end - start) == 0) { ++ size_t flag_length = strlen(flag->name); ++ if (length == flag_length ++ && memcmp(start, flag->name, length) == 0) { + /* Matched "noXXXX", so reverse the sense. */ + clear |= flag->set; + set |= flag->clear; + break; +- } else if (memcmp(start, flag->name + 2, end - start) +- == 0) { ++ } else if (length == flag_length - 2 ++ && memcmp(start, flag->name + 2, length) == 0) { + /* Matched "XXXX", so don't reverse. */ + set |= flag->set; + clear |= flag->clear; +@@ -1808,14 +1811,17 @@ ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) + while (*end != L'\0' && *end != L'\t' && + *end != L' ' && *end != L',') + end++; ++ size_t length = end - start; + for (flag = flags; flag->wname != NULL; flag++) { +- if (wmemcmp(start, flag->wname, end - start) == 0) { ++ size_t flag_length = wcslen(flag->wname); ++ if (length == flag_length ++ && wmemcmp(start, flag->wname, length) == 0) { + /* Matched "noXXXX", so reverse the sense. */ + clear |= flag->set; + set |= flag->clear; + break; +- } else if (wmemcmp(start, flag->wname + 2, end - start) +- == 0) { ++ } else if (length == flag_length - 2 ++ && wmemcmp(start, flag->wname + 2, length) == 0) { + /* Matched "XXXX", so don't reverse. */ + set |= flag->set; + clear |= flag->clear; +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8922.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8922.patch new file mode 100644 index 0000000..bfb684a --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8922.patch @@ -0,0 +1,170 @@ +From d094dc02905605ca514baf87855f026b9bf52f1f Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sun, 8 Feb 2015 13:29:51 -0800 +Subject: [PATCH] Issue 405: segfault on malformed 7z archive + +Reject a couple of nonsensical cases. +--- + Makefile.am | 3 + + libarchive/archive_read_support_format_7zip.c | 9 +++ + libarchive/test/CMakeLists.txt | 1 + + .../test/test_read_format_7zip_malformed.7z.uu | 5 ++ + libarchive/test/test_read_format_7zip_malformed.c | 67 ++++++++++++++++++++++ + .../test/test_read_format_7zip_malformed2.7z.uu | 5 ++ + 6 files changed, 90 insertions(+) + create mode 100644 libarchive/test/test_read_format_7zip_malformed.7z.uu + create mode 100644 libarchive/test/test_read_format_7zip_malformed.c + create mode 100644 libarchive/test/test_read_format_7zip_malformed2.7z.uu + +diff --git a/Makefile.am b/Makefile.am +index d6e40a2..f6e1e20 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -372,6 +372,7 @@ libarchive_test_SOURCES= \ + libarchive/test/test_read_filter_program_signature.c \ + libarchive/test/test_read_filter_uudecode.c \ + libarchive/test/test_read_format_7zip.c \ ++ libarchive/test/test_read_format_7zip_malformed.c \ + libarchive/test/test_read_format_ar.c \ + libarchive/test/test_read_format_cab.c \ + libarchive/test/test_read_format_cab_filename.c \ +@@ -599,6 +600,8 @@ libarchive_test_EXTRA_DIST=\ + libarchive/test/test_read_format_7zip_lzma1_2.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu \ + libarchive/test/test_read_format_7zip_lzma2.7z.uu \ ++ libarchive/test/test_read_format_7zip_malformed.7z.uu \ ++ libarchive/test/test_read_format_7zip_malformed2.7z.uu \ + libarchive/test/test_read_format_7zip_ppmd.7z.uu \ + libarchive/test/test_read_format_7zip_symbolic_name.7z.uu \ + libarchive/test/test_read_format_ar.ar.uu \ +diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c +index 194b8d5..e490c00 100644 +--- a/libarchive/archive_read_support_format_7zip.c ++++ b/libarchive/archive_read_support_format_7zip.c +@@ -1940,7 +1940,16 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) + return (-1); + if (1000000 < ci->dataStreamIndex) + return (-1); ++ if (ci->numFolders > 0) { ++ archive_set_error(&a->archive, -1, ++ "Malformed 7-Zip archive"); ++ goto failed; ++ } + break; ++ default: ++ archive_set_error(&a->archive, -1, ++ "Malformed 7-Zip archive"); ++ goto failed; + } + + if ((p = header_bytes(a, 1)) == NULL) +diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt +index 6ac850d..08770d9 100644 +--- a/libarchive/test/CMakeLists.txt ++++ b/libarchive/test/CMakeLists.txt +@@ -87,6 +87,7 @@ IF(ENABLE_TEST) + test_read_filter_program_signature.c + test_read_filter_uudecode.c + test_read_format_7zip.c ++ test_read_format_7zip_malformed.c + test_read_format_ar.c + test_read_format_cab.c + test_read_format_cab_filename.c +diff --git a/libarchive/test/test_read_format_7zip_malformed.7z.uu b/libarchive/test/test_read_format_7zip_malformed.7z.uu +new file mode 100644 +index 0000000..179f633 +--- /dev/null ++++ b/libarchive/test/test_read_format_7zip_malformed.7z.uu +@@ -0,0 +1,5 @@ ++begin 644 test_read_format_7zip_malformed.7z ++M-WJ\KR<<,#"@P/<&!P````````!(`````````&:^$Y +Date: Sat, 7 Feb 2015 19:03:43 -0800 +Subject: [PATCH] Issue 406: Segfault on malformed Zip archive + +Issue here was reading a size field as a signed number +and then using that as an offset. Fixed by correctly +masking the size value to an unsigned result. + +Includes test based on the archive provided in the issue report. + +--- +diff --git a/Makefile.am b/Makefile.am +index f6e1e20..fb90b9c 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -428,6 +428,7 @@ libarchive_test_SOURCES= \ + libarchive/test/test_read_format_zip_comment_stored.c \ + libarchive/test/test_read_format_zip_filename.c \ + libarchive/test/test_read_format_zip_mac_metadata.c \ ++ libarchive/test/test_read_format_zip_malformed.c \ + libarchive/test/test_read_format_zip_sfx.c \ + libarchive/test/test_read_large.c \ + libarchive/test/test_read_pax_truncated.c \ +@@ -685,6 +686,7 @@ libarchive_test_EXTRA_DIST=\ + libarchive/test/test_read_format_zip_filename_utf8_ru.zip.uu \ + libarchive/test/test_read_format_zip_length_at_end.zip.uu \ + libarchive/test/test_read_format_zip_mac_metadata.zip.uu \ ++ libarchive/test/test_read_format_zip_malformed1.zip.uu \ + libarchive/test/test_read_format_zip_sfx.uu \ + libarchive/test/test_read_format_zip_symlink.zip.uu \ + libarchive/test/test_read_format_zip_ux.zip.uu \ +diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c +index 450a6f7..1aed84a 100644 +--- a/libarchive/archive_read_support_format_zip.c ++++ b/libarchive/archive_read_support_format_zip.c +@@ -1701,7 +1701,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) + if (datasize >= 1 && p[offset] == 1) {/* version=1 */ + if (datasize >= 4) { + /* get a uid size. */ +- uidsize = p[offset+1]; ++ uidsize = 0xff & (int)p[offset+1]; + if (uidsize == 2) + zip_entry->uid = + archive_le16dec( +@@ -1713,7 +1713,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) + } + if (datasize >= (2 + uidsize + 3)) { + /* get a gid size. */ +- gidsize = p[offset+2+uidsize]; ++ gidsize = 0xff & (int)p[offset+2+uidsize]; + if (gidsize == 2) + zip_entry->gid = + archive_le16dec( +diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt +index 08770d9..2dc1740 100644 +--- a/libarchive/test/CMakeLists.txt ++++ b/libarchive/test/CMakeLists.txt +@@ -143,6 +143,7 @@ IF(ENABLE_TEST) + test_read_format_zip_comment_stored.c + test_read_format_zip_filename.c + test_read_format_zip_mac_metadata.c ++ test_read_format_zip_malformed.c + test_read_format_zip_sfx.c + test_read_large.c + test_read_pax_truncated.c +diff --git a/libarchive/test/test_read_format_zip_malformed.c b/libarchive/test/test_read_format_zip_malformed.c +new file mode 100644 +index 0000000..2327d91 +--- /dev/null ++++ b/libarchive/test/test_read_format_zip_malformed.c +@@ -0,0 +1,61 @@ ++/*- ++ * Copyright (c) 2003-2007 Tim Kientzle ++ * Copyright (c) 2011 Michihiro NAKAJIMA ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "test.h" ++__FBSDID("$FreeBSD$"); ++ ++static void ++test_malformed1(void) ++{ ++ const char *refname = "test_read_format_zip_malformed1.zip"; ++ struct archive *a; ++ struct archive_entry *ae; ++ char *p; ++ size_t s; ++ ++ extract_reference_file(refname); ++ ++ /* Verify with seeking reader. */ ++ assert((a = archive_read_new()) != NULL); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); ++ ++ /* Verify with streaming reader. */ ++ p = slurpfile(&s, refname); ++ assert((a = archive_read_new()) != NULL); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); ++ assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 31)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); ++} ++ ++DEFINE_TEST(test_read_format_zip_malformed) ++{ ++ test_malformed1(); ++} +diff --git a/libarchive/test/test_read_format_zip_malformed1.zip.uu b/libarchive/test/test_read_format_zip_malformed1.zip.uu +new file mode 100644 +index 0000000..cbd21a8 +--- /dev/null ++++ b/libarchive/test/test_read_format_zip_malformed1.zip.uu +@@ -0,0 +1,5 @@ ++begin 644 test_read_format_zip_malformed1.zip ++M4$L#!#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`$`!P`,#`P,#`P"0`P,#`P,#`P ++1,#!U>`L``80P,#`P,#`P,#`` ++` ++end +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8924.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8924.patch new file mode 100644 index 0000000..ae452be --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8924.patch @@ -0,0 +1,66 @@ +From bb9b157146a62e655fb369b32684398c949fa1b1 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 21 Feb 2015 09:36:23 -0800 +Subject: [PATCH] Issue 407: Tar reader tries to examine last character of an + empty filename + +Of interest: While working on this, I noted that we have +an existing test for tar files with empty filenames. +That test asserts that the correct behavior here is for the +format handler to return the entry with the empty filename +and a status of ARCHIVE_OK. Clients need to be robust against +empty filenames. +--- + libarchive/archive_read_support_format_tar.c | 20 ++++++++------------ + 1 file changed, 8 insertions(+), 12 deletions(-) + +diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c +index 37399a0..1e78093 100644 +--- a/libarchive/archive_read_support_format_tar.c ++++ b/libarchive/archive_read_support_format_tar.c +@@ -456,6 +456,7 @@ archive_read_format_tar_read_header(struct archive_read *a, + static int default_dev; + struct tar *tar; + const char *p; ++ const wchar_t *wp; + int r; + size_t l, unconsumed = 0; + +@@ -506,27 +507,22 @@ archive_read_format_tar_read_header(struct archive_read *a, + } + } + +- if (r == ARCHIVE_OK) { ++ if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) { + /* + * "Regular" entry with trailing '/' is really + * directory: This is needed for certain old tar + * variants and even for some broken newer ones. + */ +- const wchar_t *wp; +- wp = archive_entry_pathname_w(entry); +- if (wp != NULL) { ++ if ((wp = archive_entry_pathname_w(entry)) != NULL) { + l = wcslen(wp); +- if (archive_entry_filetype(entry) == AE_IFREG +- && wp[l-1] == L'/') ++ if (l > 0 && wp[l - 1] == L'/') { + archive_entry_set_filetype(entry, AE_IFDIR); +- } else { +- p = archive_entry_pathname(entry); +- if (p == NULL) +- return (ARCHIVE_FAILED); ++ } ++ } else if ((p = archive_entry_pathname(entry)) != NULL) { + l = strlen(p); +- if (archive_entry_filetype(entry) == AE_IFREG +- && p[l-1] == '/') ++ if (l > 0 && p[l - 1] == '/') { + archive_entry_set_filetype(entry, AE_IFDIR); ++ } + } + } + return (r); +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8925.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8925.patch new file mode 100644 index 0000000..841b90c --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8925.patch @@ -0,0 +1,166 @@ +From 1e18cbb71515a22b2a6f1eb4aaadea461929b834 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 21 Feb 2015 10:37:48 -0800 +Subject: [PATCH] Issue 408: Fix escaped newline parsing + +--- + libarchive/archive_read_support_format_mtree.c | 56 ++++++++++++------------- + libarchive/test/test_read_format_mtree.c | 8 +++- + libarchive/test/test_read_format_mtree.mtree.uu | 20 +++++---- + 3 files changed, 45 insertions(+), 39 deletions(-) + +diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c +index c4e7021..397bfee 100644 +--- a/libarchive/archive_read_support_format_mtree.c ++++ b/libarchive/archive_read_support_format_mtree.c +@@ -1661,6 +1661,10 @@ parse_escapes(char *src, struct mtree_entry *mentry) + c = '\v'; + ++src; + break; ++ case '\\': ++ c = '\\'; ++ ++src; ++ break; + } + } + *dest++ = c; +@@ -1804,8 +1808,7 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi + ssize_t total_size = 0; + ssize_t find_off = 0; + const void *t; +- const char *s; +- void *p; ++ void *nl; + char *u; + + /* Accumulate line in a line buffer. */ +@@ -1816,11 +1819,10 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi + return (0); + if (bytes_read < 0) + return (ARCHIVE_FATAL); +- s = t; /* Start of line? */ +- p = memchr(t, '\n', bytes_read); +- /* If we found '\n', trim the read. */ +- if (p != NULL) { +- bytes_read = 1 + ((const char *)p) - s; ++ nl = memchr(t, '\n', bytes_read); ++ /* If we found '\n', trim the read to end exactly there. */ ++ if (nl != NULL) { ++ bytes_read = ((const char *)nl) - ((const char *)t) + 1; + } + if (total_size + bytes_read + 1 > limit) { + archive_set_error(&a->archive, +@@ -1834,38 +1836,34 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi + "Can't allocate working buffer"); + return (ARCHIVE_FATAL); + } ++ /* Append new bytes to string. */ + memcpy(mtree->line.s + total_size, t, bytes_read); + __archive_read_consume(a, bytes_read); + total_size += bytes_read; +- /* Null terminate. */ + mtree->line.s[total_size] = '\0'; +- /* If we found an unescaped '\n', clean up and return. */ ++ + for (u = mtree->line.s + find_off; *u; ++u) { + if (u[0] == '\n') { ++ /* Ends with unescaped newline. */ + *start = mtree->line.s; + return total_size; +- } +- if (u[0] == '#') { +- if (p == NULL) ++ } else if (u[0] == '#') { ++ /* Ends with comment sequence #...\n */ ++ if (nl == NULL) { ++ /* But we've not found the \n yet */ + break; +- *start = mtree->line.s; +- return total_size; +- } +- if (u[0] != '\\') +- continue; +- if (u[1] == '\\') { +- ++u; +- continue; +- } +- if (u[1] == '\n') { +- memmove(u, u + 1, +- total_size - (u - mtree->line.s) + 1); +- --total_size; +- ++u; +- break; ++ } ++ } else if (u[0] == '\\') { ++ if (u[1] == '\n') { ++ /* Trim escaped newline. */ ++ total_size -= 2; ++ mtree->line.s[total_size] = '\0'; ++ break; ++ } else if (u[1] != '\0') { ++ /* Skip the two-char escape sequence */ ++ ++u; ++ } + } +- if (u[1] == '\0') +- break; + } + find_off = u - mtree->line.s; + } +diff --git a/libarchive/test/test_read_format_mtree.c b/libarchive/test/test_read_format_mtree.c +index efedc41..85a0e2e 100644 +--- a/libarchive/test/test_read_format_mtree.c ++++ b/libarchive/test/test_read_format_mtree.c +@@ -110,6 +110,10 @@ test_read_format_mtree1(void) + assertEqualString(archive_entry_pathname(ae), "dir2/dir3b/indir3b"); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); ++ assertEqualString(archive_entry_pathname(ae), "dir2/dir3b/filename\\with_esc\b\t\fapes"); ++ assertEqualInt(archive_entry_filetype(ae), AE_IFREG); ++ ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "notindir"); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); +@@ -151,7 +155,7 @@ test_read_format_mtree1(void) + assertEqualInt(archive_entry_mtime(ae), min_time); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); +- assertEqualInt(19, archive_file_count(a)); ++ assertEqualInt(20, archive_file_count(a)); + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + } +diff --git a/libarchive/test/test_read_format_mtree.mtree.uu b/libarchive/test/test_read_format_mtree.mtree.uu +index a0dff18..f1c9d60 100644 +--- a/libarchive/test/test_read_format_mtree.mtree.uu ++++ b/libarchive/test/test_read_format_mtree.mtree.uu +@@ -5,14 +5,16 @@ M92!U:60],3@*("XN"F9I;&5<,#0P=VET:%PP-#!S<&%C92!T>7!E/69I;&4* + M9&ER,B!T>7!E/61I<@H@9&ER,V$@='EP93UD:7(*("!I;F1I7!E/61I<@H@(&EN9&ER,V(@ +-M='EP93UF:6QE"B`@+BX*("XN"FYO=&EN9&ER('1Y<&4]9FEL90ID:7(R+V9U +-M;&QI;F1I7!E/69I;&4@F4]+3$*9&ER,B]B:6=F:6QE('1Y<&4]9FEL92!S:7IE/3DR +-M,C,S-S(P,S8X-30W-S4X,#<*9&ER,B]T;V]B:6=F:6QE('1Y<&4]9FEL92!S +-M:7IE/3DR,C,S-S(P,S8X-30W-S4X,#@*9&ER,B]V97)Y;VQD9FEL92!T>7!E +-M/69I;&4@=&EM93TM.3(R,S,W,C`S-C@U-#7!E/69I ++M;&4@F4]+3$*9&ER,B]B:6=F:6QE('1Y ++M<&4]9FEL92!S:7IE/3DR,C,S-S(P,S8X-30W-S4X,#<*9&ER,B]T;V]B:6=F ++M:6QE('1Y<&4]9FEL92!S:7IE/3DR,C,S-S(P,S8X-30W-S4X,#@*9&ER,B]V ++M97)Y;VQD9FEL92!T>7!E/69I;&4@=&EM93TM.3(R,S,W,C`S-C@U-# +Date: Tue, 3 Mar 2015 20:17:37 -0800 +Subject: [PATCH] Issue 410: Segfault on invalid rar archive + +Libarchive's API passes a void ** which is set by the format +to the address of the entry data that was just read. + +In one particular case, the RAR decompression logic uses a +non-NULL value here to indicate that the internal 128k decompression +buffer has been filled. But the RAR code took no steps to ensure +that the value was set NULL on entry. As a result, a crafted RAR +file can trick libarchive into returning to the caller a 128k block +of data starting at whatever value was previously in the caller's +variable. + +The fix is simply to set *buff = NULL on entry to the RAR +decompression logic. +--- + libarchive/archive_read_support_format_rar.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c +index 3e7412f..ee8ce53 100644 +--- a/libarchive/archive_read_support_format_rar.c ++++ b/libarchive/archive_read_support_format_rar.c +@@ -1002,8 +1002,8 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, + rar->bytes_unconsumed = 0; + } + ++ *buff = NULL; + if (rar->entry_eof || rar->offset_seek >= rar->unp_size) { +- *buff = NULL; + *size = 0; + *offset = rar->offset; + if (*offset < rar->unp_size) +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8928.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8928.patch new file mode 100644 index 0000000..a4c8244 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8928.patch @@ -0,0 +1,106 @@ +From f667f381486371ccb19f2b1e7307a695cf3a5279 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sun, 3 Apr 2016 11:03:22 -0700 +Subject: [PATCH] Issue 550: Fix out-of-bounds read in mtree. + +The mtree parser scanned from the end of the string to identify +the filename when the filename is the last element of the line. +If the filename was the entire line, the logic would scan back +to before the start of the string. + +The revised logic scans from the beginning of the string +and remembers the last separator position to locate the +trailing filename. +--- + libarchive/archive_read_support_format_mtree.c | 53 ++++++++++++++------------ + 1 file changed, 29 insertions(+), 24 deletions(-) + +diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c +index 397bfee..a672cb3 100644 +--- a/libarchive/archive_read_support_format_mtree.c ++++ b/libarchive/archive_read_support_format_mtree.c +@@ -826,8 +826,8 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, + struct mtree_entry *entry; + struct mtree_option *iter; + const char *next, *eq, *name, *end; +- size_t len; +- int r; ++ size_t name_len, len; ++ int r, i; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + archive_set_error(&a->archive, errno, "Can't allocate memory"); +@@ -847,43 +847,48 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, + *last_entry = entry; + + if (is_form_d) { +- /* +- * This form places the file name as last parameter. +- */ +- name = line + line_len -1; ++ /* Filename is last item on line. */ ++ /* Adjust line_len to trim trailing whitespace */ + while (line_len > 0) { +- if (*name != '\r' && *name != '\n' && +- *name != '\t' && *name != ' ') ++ char last_character = line[line_len - 1]; ++ if (last_character == '\r' ++ || last_character == '\n' ++ || last_character == '\t' ++ || last_character == ' ') { ++ line_len--; ++ } else { + break; +- name--; +- line_len--; ++ } + } +- len = 0; +- while (line_len > 0) { +- if (*name == '\r' || *name == '\n' || +- *name == '\t' || *name == ' ') { +- name++; +- break; ++ /* Name starts after the last whitespace separator */ ++ name = line; ++ for (i = 0; i < line_len; i++) { ++ if (line[i] == '\r' ++ || line[i] == '\n' ++ || line[i] == '\t' ++ || line[i] == ' ') { ++ name = line + i + 1; + } +- name--; +- line_len--; +- len++; + } ++ name_len = line + line_len - name; + end = name; + } else { +- len = strcspn(line, " \t\r\n"); ++ /* Filename is first item on line */ ++ name_len = strcspn(line, " \t\r\n"); + name = line; +- line += len; ++ line += name_len; + end = line + line_len; + } ++ /* name/name_len is the name within the line. */ ++ /* line..end brackets the entire line except the name */ + +- if ((entry->name = malloc(len + 1)) == NULL) { ++ if ((entry->name = malloc(name_len + 1)) == NULL) { + archive_set_error(&a->archive, errno, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + +- memcpy(entry->name, name, len); +- entry->name[len] = '\0'; ++ memcpy(entry->name, name, name_len); ++ entry->name[name_len] = '\0'; + parse_escapes(entry->name, entry); + + for (iter = *global; iter != NULL; iter = iter->next) { +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8930.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8930.patch new file mode 100644 index 0000000..e7d44ab --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8930.patch @@ -0,0 +1,129 @@ +From 139c528ab8016711b6b59a4460afd0d6b236beb5 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 11 Apr 2015 22:44:12 -0700 +Subject: [PATCH] This is a combination of 2 commits. + +== The first commit's message is: == + +Issue #522: Dir loop in malformed ISO causes segfault + +Github Issue #522 revealed that we could blow the stack +when recursing to assemble ISO paths. I saw this happen +at 130,000 dir levels. This patch addresses this by limiting +the directory recursion to 1,000 elements. + +TODO: It would be even better to track and detect the dir loop +directly. + +== This is the 2nd commit message: == + +Github Issue #522: Detect cycles in the ISO directory tree +--- + libarchive/archive_read_support_format_iso9660.c | 43 +++++++++++++++++++----- + 1 file changed, 34 insertions(+), 9 deletions(-) + +diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c +index 47268a2..6934cee 100644 +--- a/libarchive/archive_read_support_format_iso9660.c ++++ b/libarchive/archive_read_support_format_iso9660.c +@@ -387,7 +387,7 @@ static int archive_read_format_iso9660_read_data(struct archive_read *, + static int archive_read_format_iso9660_read_data_skip(struct archive_read *); + static int archive_read_format_iso9660_read_header(struct archive_read *, + struct archive_entry *); +-static const char *build_pathname(struct archive_string *, struct file_info *); ++static const char *build_pathname(struct archive_string *, struct file_info *, int); + static int build_pathname_utf16be(unsigned char *, size_t, size_t *, + struct file_info *); + #if DEBUG +@@ -1225,6 +1225,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname is too long"); ++ return (ARCHIVE_FATAL); + } + + r = archive_entry_copy_pathname_l(entry, +@@ -1247,9 +1248,16 @@ archive_read_format_iso9660_read_header(struct archive_read *a, + rd_r = ARCHIVE_WARN; + } + } else { +- archive_string_empty(&iso9660->pathname); +- archive_entry_set_pathname(entry, +- build_pathname(&iso9660->pathname, file)); ++ const char *path = build_pathname(&iso9660->pathname, file, 0); ++ if (path == NULL) { ++ archive_set_error(&a->archive, ++ ARCHIVE_ERRNO_FILE_FORMAT, ++ "Pathname is too long"); ++ return (ARCHIVE_FATAL); ++ } else { ++ archive_string_empty(&iso9660->pathname); ++ archive_entry_set_pathname(entry, path); ++ } + } + + iso9660->entry_bytes_remaining = file->size; +@@ -1744,12 +1752,12 @@ parse_file_info(struct archive_read *a, struct file_info *parent, + const unsigned char *isodirrec) + { + struct iso9660 *iso9660; +- struct file_info *file; ++ struct file_info *file, *filep; + size_t name_len; + const unsigned char *rr_start, *rr_end; + const unsigned char *p; + size_t dr_len; +- uint64_t fsize; ++ uint64_t fsize, offset; + int32_t location; + int flags; + +@@ -1793,6 +1801,16 @@ parse_file_info(struct archive_read *a, struct file_info *parent, + return (NULL); + } + ++ /* Sanity check that this entry does not create a cycle. */ ++ offset = iso9660->logical_block_size * (uint64_t)location; ++ for (filep = parent; filep != NULL; filep = filep->parent) { ++ if (filep->offset == offset) { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Directory structure contains loop"); ++ return (NULL); ++ } ++ } ++ + /* Create a new file entry and copy data from the ISO dir record. */ + file = (struct file_info *)calloc(1, sizeof(*file)); + if (file == NULL) { +@@ -1801,7 +1819,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, + return (NULL); + } + file->parent = parent; +- file->offset = iso9660->logical_block_size * (uint64_t)location; ++ file->offset = offset; + file->size = fsize; + file->mtime = isodate7(isodirrec + DR_date_offset); + file->ctime = file->atime = file->mtime; +@@ -3169,10 +3187,17 @@ time_from_tm(struct tm *t) + } + + static const char * +-build_pathname(struct archive_string *as, struct file_info *file) ++build_pathname(struct archive_string *as, struct file_info *file, int depth) + { ++ // Plain ISO9660 only allows 8 dir levels; if we get ++ // to 1000, then something is very, very wrong. ++ if (depth > 1000) { ++ return NULL; ++ } + if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) { +- build_pathname(as, file->parent); ++ if (build_pathname(as, file->parent, depth + 1) == NULL) { ++ return NULL; ++ } + archive_strcat(as, "/"); + } + if (archive_strlen(&file->name) == 0) +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8931.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8931.patch new file mode 100644 index 0000000..4c04473 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8931.patch @@ -0,0 +1,94 @@ +From 11f6da24b13840397fee87445859d7f2a2ac02f8 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 16 May 2015 12:16:28 -0700 +Subject: [PATCH] This is a combination of 2 commits. == The first commit's + message is: == + +Issue #539: Try a different way to compute max/min time_t values. + +== This is the 2nd commit message: == + +Don't try to be smart about probing the min/max tim_t values. +Just assume that a signed time_t is really a 64-bit or 32-bit integer. +--- + libarchive/archive_read_support_format_mtree.c | 47 ++++++++++++++------------ + 1 file changed, 25 insertions(+), 22 deletions(-) + +diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c +index 64d5e67..3abe198 100644 +--- a/libarchive/archive_read_support_format_mtree.c ++++ b/libarchive/archive_read_support_format_mtree.c +@@ -139,16 +139,22 @@ get_time_t_max(void) + #if defined(TIME_T_MAX) + return TIME_T_MAX; + #else +- static time_t t; +- time_t a; +- if (t == 0) { +- a = 1; +- while (a > t) { +- t = a; +- a = a * 2 + 1; ++ /* ISO C allows time_t to be a floating-point type, ++ but POSIX requires an integer type. The following ++ should work on any system that follows the POSIX ++ conventions. */ ++ if (((time_t)0) < ((time_t)-1)) { ++ /* Time_t is unsigned */ ++ return (~(time_t)0); ++ } else { ++ /* Time_t is signed. */ ++ /* Assume it's the same as int64_t or int32_t */ ++ if (sizeof(time_t) == sizeof(int64_t)) { ++ return (time_t)INT64_MAX; ++ } else { ++ return (time_t)INT32_MAX; + } + } +- return t; + #endif + } + +@@ -158,20 +164,17 @@ get_time_t_min(void) + #if defined(TIME_T_MIN) + return TIME_T_MIN; + #else +- /* 't' will hold the minimum value, which will be zero (if +- * time_t is unsigned) or -2^n (if time_t is signed). */ +- static int computed; +- static time_t t; +- time_t a; +- if (computed == 0) { +- a = (time_t)-1; +- while (a < t) { +- t = a; +- a = a * 2; +- } +- computed = 1; ++ if (((time_t)0) < ((time_t)-1)) { ++ /* Time_t is unsigned */ ++ return (time_t)0; ++ } else { ++ /* Time_t is signed. */ ++ if (sizeof(time_t) == sizeof(int64_t)) { ++ return (time_t)INT64_MIN; ++ } else { ++ return (time_t)INT32_MIN; ++ } + } +- return t; + #endif + } + +@@ -1562,7 +1565,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, + int64_t m; + int64_t my_time_t_max = get_time_t_max(); + int64_t my_time_t_min = get_time_t_min(); +- long ns; ++ long ns = 0; + + *parsed_kws |= MTREE_HAS_MTIME; + m = mtree_atol10(&val); +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8932.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8932.patch new file mode 100644 index 0000000..a4b3f85 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8932.patch @@ -0,0 +1,178 @@ +From 9ef77b3d5e04de8323bdc61c1cb7ea77613e4a38 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 8 Aug 2015 21:47:43 -0700 +Subject: [PATCH] This is a combination of 2 commits. == The first commit's + message is: == + +Issue 547: problems with compress bidder + +The code previously was not very careful about verifying the +compression parameters. This led to cases where it failed to +reject invalid compressed data at the beginning. The invalid +left shift was one symptom of this. + +The code is now more careful: It verifies that the compression +parameter byte exists and verifies that the maximum code size +is <= 16 bits. + +This also includes some new tests to verify that truncated or +otherwise invalid compressed data is rejected. + +== This is the 2nd commit message: == + +add missing tests to automake +--- + Makefile.am | 1 + + libarchive/archive_read_support_filter_compress.c | 21 ++++-- + libarchive/test/test_read_filter_compress.c | 80 +++++++++++++++++++++++ + 3 files changed, 96 insertions(+), 6 deletions(-) + create mode 100644 libarchive/test/test_read_filter_compress.c + +diff --git a/Makefile.am b/Makefile.am +index fb90b9c..e088b75 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -364,6 +364,7 @@ libarchive_test_SOURCES= \ + libarchive/test/test_read_disk_entry_from_file.c \ + libarchive/test/test_read_extract.c \ + libarchive/test/test_read_file_nonexistent.c \ ++ libarchive/test/test_read_filter_compress.c \ + libarchive/test/test_read_filter_grzip.c \ + libarchive/test/test_read_filter_lrzip.c \ + libarchive/test/test_read_filter_lzop.c \ +diff --git a/libarchive/archive_read_support_filter_compress.c b/libarchive/archive_read_support_filter_compress.c +index 3f5d1f3..6fa9993 100644 +--- a/libarchive/archive_read_support_filter_compress.c ++++ b/libarchive/archive_read_support_filter_compress.c +@@ -185,19 +185,22 @@ compress_bidder_bid(struct archive_read_filter_bidder *self, + + (void)self; /* UNUSED */ + +- buffer = __archive_read_filter_ahead(filter, 2, &avail); ++ /* Shortest valid compress file is 3 bytes. */ ++ buffer = __archive_read_filter_ahead(filter, 3, &avail); + + if (buffer == NULL) + return (0); + + bits_checked = 0; ++ /* First two bytes are the magic value */ + if (buffer[0] != 0x1F || buffer[1] != 0x9D) + return (0); +- bits_checked += 16; +- +- /* +- * TODO: Verify more. +- */ ++ /* Third byte holds compression parameters. */ ++ if (buffer[2] & 0x20) /* Reserved bit, must be zero. */ ++ return (0); ++ if (buffer[2] & 0x40) /* Reserved bit, must be zero. */ ++ return (0); ++ bits_checked += 18; + + return (bits_checked); + } +@@ -239,7 +242,13 @@ compress_bidder_init(struct archive_read_filter *self) + (void)getbits(self, 8); /* Skip first signature byte. */ + (void)getbits(self, 8); /* Skip second signature byte. */ + ++ /* Get compression parameters. */ + code = getbits(self, 8); ++ if ((code & 0x1f) > 16) { ++ archive_set_error(&self->archive->archive, -1, ++ "Invalid compressed data"); ++ return (ARCHIVE_FATAL); ++ } + state->maxcode_bits = code & 0x1f; + state->maxcode = (1 << state->maxcode_bits); + state->use_reset_code = code & 0x80; +diff --git a/libarchive/test/test_read_filter_compress.c b/libarchive/test/test_read_filter_compress.c +new file mode 100644 +index 0000000..03a1d5f +--- /dev/null ++++ b/libarchive/test/test_read_filter_compress.c +@@ -0,0 +1,80 @@ ++/*- ++ * Copyright (c) 2003-2008 Tim Kientzle ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "test.h" ++ ++DEFINE_TEST(test_read_filter_compress_truncated) ++{ ++ const char data[] = {0x1f, 0x9d}; ++ struct archive *a; ++ ++ assert((a = archive_read_new()) != NULL); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_compress(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); ++ assertEqualIntA(a, ARCHIVE_FATAL, ++ archive_read_open_memory(a, data, sizeof(data))); ++ ++ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); ++ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); ++} ++ ++ ++DEFINE_TEST(test_read_filter_compress_empty2) ++{ ++ const char data[] = {0x1f, 0x9d, 0x10}; ++ struct archive *a; ++ struct archive_entry *ae; ++ ++ assert((a = archive_read_new()) != NULL); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_compress(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); ++ assertEqualIntA(a, ARCHIVE_OK, ++ archive_read_open_memory(a, data, sizeof(data))); ++ ++ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); ++ ++ /* Verify that the format detection worked. */ ++ assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_COMPRESS); ++ assertEqualString(archive_filter_name(a, 0), "compress (.Z)"); ++ assertEqualInt(archive_format(a), ARCHIVE_FORMAT_EMPTY); ++ ++ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); ++ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); ++} ++ ++ ++DEFINE_TEST(test_read_filter_compress_invalid) ++{ ++ const char data[] = {0x1f, 0x9d, 0x11}; ++ struct archive *a; ++ ++ assert((a = archive_read_new()) != NULL); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_compress(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); ++ assertEqualIntA(a, ARCHIVE_FATAL, ++ archive_read_open_memory(a, data, sizeof(data))); ++ ++ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); ++ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); ++} +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2015-8934.patch b/SOURCES/libarchive-3.1.2-CVE-2015-8934.patch new file mode 100644 index 0000000..9babdea --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2015-8934.patch @@ -0,0 +1,145 @@ +From 470ceb47fe072d10c4b5d02dba3a8b7b3ce731e5 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sun, 19 Jun 2016 15:31:46 -0700 +Subject: [PATCH] Issue 521: Properly check reading from lzss decompression + buffer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Prior code could be tricked into trying to copy data +from beyond the end of the internal decompression buffer. + +Thanks to Hanno Böck for his ongoing fuzz-testing work with libarchive. +--- + Makefile.am | 1 + + libarchive/archive_read_support_format_rar.c | 12 ++++-- + libarchive/test/CMakeLists.txt | 1 + + libarchive/test/test_read_format_rar_invalid1.c | 44 ++++++++++++++++++++++ + .../test/test_read_format_rar_invalid1.rar.uu | 5 +++ + 5 files changed, 59 insertions(+), 4 deletions(-) + create mode 100644 libarchive/test/test_read_format_rar_invalid1.c + create mode 100644 libarchive/test/test_read_format_rar_invalid1.rar.uu + +diff --git a/Makefile.am b/Makefile.am +index e088b75..40ac1d1 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -414,6 +414,7 @@ libarchive_test_SOURCES= \ + libarchive/test/test_read_format_mtree.c \ + libarchive/test/test_read_format_pax_bz2.c \ + libarchive/test/test_read_format_rar.c \ ++ libarchive/test/test_read_format_rar_invalid1.c \ + libarchive/test/test_read_format_raw.c \ + libarchive/test/test_read_format_tar.c \ + libarchive/test/test_read_format_tar_empty_filename.c \ +diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c +index 94cd108..c06a32b 100644 +--- a/libarchive/archive_read_support_format_rar.c ++++ b/libarchive/archive_read_support_format_rar.c +@@ -2798,11 +2798,10 @@ copy_from_lzss_window(struct archive_read *a, const void **buffer, + } + + windowoffs = lzss_offset_for_position(&rar->lzss, startpos); +- if(windowoffs + length <= lzss_size(&rar->lzss)) ++ if(windowoffs + length <= lzss_size(&rar->lzss)) { + memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], + length); +- else +- { ++ } else if (length <= lzss_size(&rar->lzss)) { + firstpart = lzss_size(&rar->lzss) - windowoffs; + if (firstpart < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, +@@ -2814,9 +2813,14 @@ copy_from_lzss_window(struct archive_read *a, const void **buffer, + &rar->lzss.window[windowoffs], firstpart); + memcpy(&rar->unp_buffer[rar->unp_offset + firstpart], + &rar->lzss.window[0], length - firstpart); +- } else ++ } else { + memcpy(&rar->unp_buffer[rar->unp_offset], + &rar->lzss.window[windowoffs], length); ++ } ++ } else { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Bad RAR file data"); ++ return (ARCHIVE_FATAL); + } + rar->unp_offset += length; + if (rar->unp_offset >= rar->unp_buffer_size) +diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt +index 2dc1740..3751da9 100644 +--- a/libarchive/test/CMakeLists.txt ++++ b/libarchive/test/CMakeLists.txt +@@ -128,6 +128,7 @@ IF(ENABLE_TEST) + test_read_format_mtree.c + test_read_format_pax_bz2.c + test_read_format_rar.c ++ test_read_format_rar_invalid1.c + test_read_format_raw.c + test_read_format_tar.c + test_read_format_tar_empty_filename.c +diff --git a/libarchive/test/test_read_format_rar_invalid1.c b/libarchive/test/test_read_format_rar_invalid1.c +new file mode 100644 +index 0000000..61dea16 +--- /dev/null ++++ b/libarchive/test/test_read_format_rar_invalid1.c +@@ -0,0 +1,44 @@ ++/*- ++ * Copyright (c) 2003-2016 Tim Kientzle ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "test.h" ++__FBSDID("$FreeBSD$"); ++ ++DEFINE_TEST(test_read_format_rar_invalid1) ++{ ++ const char *refname = "test_read_format_rar_invalid1.rar"; ++ struct archive *a; ++ struct archive_entry *ae; ++ char *buff[100]; ++ ++ extract_reference_file(refname); ++ assert((a = archive_read_new()) != NULL); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); ++ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buff, 99)); ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); ++ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); ++} +diff --git a/libarchive/test/test_read_format_rar_invalid1.rar.uu b/libarchive/test/test_read_format_rar_invalid1.rar.uu +new file mode 100644 +index 0000000..2380399 +--- /dev/null ++++ b/libarchive/test/test_read_format_rar_invalid1.rar.uu +@@ -0,0 +1,5 @@ ++begin 644 test_read_format_rar_invalid1.rar ++M4F%R(1H'`,^0B$4= ++2,P0`I($``'1E +Date: Sun, 24 Apr 2016 17:13:45 -0700 +Subject: [PATCH] Issue #656: Fix CVE-2016-1541, VU#862384 + +When reading OS X metadata entries in Zip archives that were stored +without compression, libarchive would use the uncompressed entry size +to allocate a buffer but would use the compressed entry size to limit +the amount of data copied into that buffer. Since the compressed +and uncompressed sizes are provided by data in the archive itself, +an attacker could manipulate these values to write data beyond +the end of the allocated buffer. + +This fix provides three new checks to guard against such +manipulation and to make libarchive generally more robust when +handling this type of entry: + 1. If an OS X metadata entry is stored without compression, + abort the entire archive if the compressed and uncompressed + data sizes do not match. + 2. When sanity-checking the size of an OS X metadata entry, + abort this entry if either the compressed or uncompressed + size is larger than 4MB. + 3. When copying data into the allocated buffer, check the copy + size against both the compressed entry size and uncompressed + entry size. +--- + libarchive/archive_read_support_format_zip.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c +index 1aed84a..fa3db11 100644 +--- a/libarchive/archive_read_support_format_zip.c ++++ b/libarchive/archive_read_support_format_zip.c +@@ -560,6 +560,11 @@ zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, + + switch(rsrc->compression) { + case 0: /* No compression. */ ++ if (rsrc->uncompressed_size != rsrc->compressed_size) { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Malformed OS X metadata entry: inconsistent size"); ++ return (ARCHIVE_FATAL); ++ } + #ifdef HAVE_ZLIB_H + case 8: /* Deflate compression. */ + #endif +@@ -580,6 +585,12 @@ zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, + (intmax_t)rsrc->uncompressed_size); + return (ARCHIVE_WARN); + } ++ if (rsrc->compressed_size > (4 * 1024 * 1024)) { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Mac metadata is too large: %jd > 4M bytes", ++ (intmax_t)rsrc->compressed_size); ++ return (ARCHIVE_WARN); ++ } + + metadata = malloc((size_t)rsrc->uncompressed_size); + if (metadata == NULL) { +@@ -619,6 +630,8 @@ zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, + bytes_avail = remaining_bytes; + switch(rsrc->compression) { + case 0: /* No compression. */ ++ if ((size_t)bytes_avail > metadata_bytes) ++ bytes_avail = metadata_bytes; + memcpy(mp, p, bytes_avail); + bytes_used = (size_t)bytes_avail; + metadata_bytes -= bytes_used; +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2016-4300.patch b/SOURCES/libarchive-3.1.2-CVE-2016-4300.patch new file mode 100644 index 0000000..34bfd05 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2016-4300.patch @@ -0,0 +1,32 @@ +From f084b0568ac2845f12a9a34e0636811d49d6a2a8 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sun, 19 Jun 2016 14:14:09 -0700 +Subject: [PATCH] Issue #718: Fix TALOS-CAN-152 + +If a 7-Zip archive declares a rediculously large number of substreams, +it can overflow an internal counter, leading a subsequent memory +allocation to be too small for the substream data. + +Thanks to the Open Source and Threat Intelligence project at Cisco +for reporting this issue. +--- + libarchive/archive_read_support_format_7zip.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c +index e490c00..8ae8443 100644 +--- a/libarchive/archive_read_support_format_7zip.c ++++ b/libarchive/archive_read_support_format_7zip.c +@@ -2054,6 +2054,9 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, + return (-1); + if (1000000 < f[i].numUnpackStreams) + return (-1); ++ if (unpack_streams > SIZE_MAX - 1000000) { ++ return (-1); ++ } + unpack_streams += (size_t)f[i].numUnpackStreams; + } + if ((p = header_bytes(a, 1)) == NULL) +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2016-4302.patch b/SOURCES/libarchive-3.1.2-CVE-2016-4302.patch new file mode 100644 index 0000000..d20fefc --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2016-4302.patch @@ -0,0 +1,35 @@ +From 05caadc7eedbef471ac9610809ba683f0c698700 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sun, 19 Jun 2016 14:21:42 -0700 +Subject: [PATCH] Issue 719: Fix for TALOS-CAN-154 + +A RAR file with an invalid zero dictionary size was not being +rejected, leading to a zero-sized allocation for the dictionary +storage which was then overwritten during the dictionary initialization. + +Thanks to the Open Source and Threat Intelligence project at Cisco for +reporting this. +--- + libarchive/archive_read_support_format_rar.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c +index 6450aac..6c49f1a 100644 +--- a/libarchive/archive_read_support_format_rar.c ++++ b/libarchive/archive_read_support_format_rar.c +@@ -2127,6 +2127,12 @@ parse_codes(struct archive_read *a) + rar->range_dec.Stream = &rar->bytein; + __archive_ppmd7_functions.Ppmd7_Construct(&rar->ppmd7_context); + ++ if (rar->dictionary_size == 0) { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Invalid zero dictionary size"); ++ return (ARCHIVE_FATAL); ++ } ++ + if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context, + rar->dictionary_size, &g_szalloc)) + { +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2016-4809.patch b/SOURCES/libarchive-3.1.2-CVE-2016-4809.patch new file mode 100644 index 0000000..22d7489 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2016-4809.patch @@ -0,0 +1,28 @@ +From fd7e0c02e272913a0a8b6d492c7260dfca0b1408 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 14 May 2016 12:37:37 -0700 +Subject: [PATCH] Reject cpio symlinks that exceed 1MB + +--- + libarchive/archive_read_support_format_cpio.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c +index c2ca85b..b09db0e 100644 +--- a/libarchive/archive_read_support_format_cpio.c ++++ b/libarchive/archive_read_support_format_cpio.c +@@ -401,6 +401,11 @@ archive_read_format_cpio_read_header(struct archive_read *a, + + /* If this is a symlink, read the link contents. */ + if (archive_entry_filetype(entry) == AE_IFLNK) { ++ if (cpio->entry_bytes_remaining > 1024 * 1024) { ++ archive_set_error(&a->archive, ENOMEM, ++ "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte"); ++ return (ARCHIVE_FATAL); ++ } + h = __archive_read_ahead(a, + (size_t)cpio->entry_bytes_remaining, NULL); + if (h == NULL) +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2016-5418-variation.patch b/SOURCES/libarchive-3.1.2-CVE-2016-5418-variation.patch new file mode 100644 index 0000000..43f6ac8 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2016-5418-variation.patch @@ -0,0 +1,150 @@ +commit 3c378bb5e4ea8d4c9a5345083f278882322f9c9b +Author: Doran Moppert +Date: Fri Aug 12 13:58:57 2016 +0930 + + Fix for hardlinks with .. in target path + - factor cleanup_pathname into cleanup_pathname_fsobj + - rename check_path_for_symlinks to check_path_fsobj for consistency + +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index 66fc0f5..37261a5 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -326,13 +326,14 @@ struct archive_write_disk { + + #define HFS_BLOCKS(s) ((s) >> 12) + +-static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags); ++static int check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); + static int check_symlinks(struct archive_write_disk *); + static int create_filesystem_object(struct archive_write_disk *); + static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); + #if defined(HAVE_FCHDIR) && defined(PATH_MAX) + static void edit_deep_directories(struct archive_write_disk *ad); + #endif ++static int cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); + static int cleanup_pathname(struct archive_write_disk *); + static int create_dir(struct archive_write_disk *, char *); + static int create_parent_dir(struct archive_write_disk *, char *); +@@ -1997,7 +1998,7 @@ create_filesystem_object(struct archive_write_disk *a) + const char *linkname; + mode_t final_mode, mode; + int r; +- /* these for check_path_for_symlinks */ ++ /* these for check_symlinks_fsobj */ + char *linkname_copy; /* non-const copy of linkname */ + struct archive_string error_string; + int error_number; +@@ -2014,13 +2015,22 @@ create_filesystem_object(struct archive_write_disk *a) + if (linkname_copy == NULL) { + return (EPERM); + } +- r = check_path_for_symlinks(linkname_copy, &error_number, &error_string, a->flags); +- free(linkname_copy); ++ /* TODO: consider using the cleaned-up path as the link target? */ ++ r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); ++ if (r != ARCHIVE_OK) { ++ archive_set_error(&a->archive, error_number, "%s", error_string.s); ++ free(linkname_copy); ++ /* EPERM is more appropriate than error_number for our callers */ ++ return (EPERM); ++ } ++ r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, error_number, "%s", error_string.s); ++ free(linkname_copy); + /* EPERM is more appropriate than error_number for our callers */ + return (EPERM); + } ++ free(linkname_copy); + r = link(linkname, a->name) ? errno : 0; + /* + * New cpio and pax formats allow hardlink entries +@@ -2365,7 +2375,8 @@ current_fixup(struct archive_write_disk *a, const char *pathname) + * Checks the given path to see if any elements along it are symlinks. Returns + * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. + */ +-static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags) ++static int ++check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) + { + #if !defined(HAVE_LSTAT) + /* Platform doesn't have lstat, so we can't look for symlinks. */ +@@ -2558,7 +2569,7 @@ check_symlinks(struct archive_write_disk *a) + int error_number; + int rc; + archive_string_init(&error_string); +- rc = check_path_for_symlinks(a->name, &error_number, &error_string, a->flags); ++ rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); + if (rc != ARCHIVE_OK) { + archive_set_error(&a->archive, error_number, "%s", error_string.s); + } +@@ -2640,15 +2651,17 @@ cleanup_pathname_win(struct archive_write_disk *a) + * set) any '..' in the path. + */ + static int +-cleanup_pathname(struct archive_write_disk *a) ++cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) + { + char *dest, *src; + char separator = '\0'; + +- dest = src = a->name; ++ dest = src = path; + if (*src == '\0') { +- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, +- "Invalid empty pathname"); ++ if (error_number) *error_number = ARCHIVE_ERRNO_MISC; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Invalid empty pathname"); + return (ARCHIVE_FAILED); + } + +@@ -2679,10 +2692,11 @@ cleanup_pathname(struct archive_write_disk *a) + } else if (src[1] == '.') { + if (src[2] == '/' || src[2] == '\0') { + /* Conditionally warn about '..' */ +- if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { +- archive_set_error(&a->archive, +- ARCHIVE_ERRNO_MISC, +- "Path contains '..'"); ++ if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { ++ if (error_number) *error_number = ARCHIVE_ERRNO_MISC; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Path contains '..'"); + return (ARCHIVE_FAILED); + } + } +@@ -2713,7 +2727,7 @@ cleanup_pathname(struct archive_write_disk *a) + * We've just copied zero or more path elements, not including the + * final '/'. + */ +- if (dest == a->name) { ++ if (dest == path) { + /* + * Nothing got copied. The path must have been something + * like '.' or '/' or './' or '/././././/./'. +@@ -2728,6 +2742,21 @@ cleanup_pathname(struct archive_write_disk *a) + return (ARCHIVE_OK); + } + ++static int ++cleanup_pathname(struct archive_write_disk *a) ++{ ++ struct archive_string error_string; ++ int error_number; ++ int rc; ++ archive_string_init(&error_string); ++ rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); ++ if (rc != ARCHIVE_OK) { ++ archive_set_error(&a->archive, error_number, "%s", error_string.s); ++ } ++ archive_string_free(&error_string); ++ return rc; ++} ++ + /* + * Create the parent directory of the specified path, assuming path + * is already in mutable storage. diff --git a/SOURCES/libarchive-3.1.2-CVE-2016-5418.patch b/SOURCES/libarchive-3.1.2-CVE-2016-5418.patch new file mode 100644 index 0000000..4b9cd7e --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2016-5418.patch @@ -0,0 +1,324 @@ +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index bbd50a6..66fc0f5 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -326,6 +326,7 @@ struct archive_write_disk { + + #define HFS_BLOCKS(s) ((s) >> 12) + ++static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags); + static int check_symlinks(struct archive_write_disk *); + static int create_filesystem_object(struct archive_write_disk *); + static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); +@@ -1791,7 +1792,7 @@ edit_deep_directories(struct archive_write_disk *a) + char *tail = a->name; + + /* If path is short, avoid the open() below. */ +- if (strlen(tail) <= PATH_MAX) ++ if (strlen(tail) < PATH_MAX) + return; + + /* Try to record our starting dir. */ +@@ -1801,7 +1802,7 @@ edit_deep_directories(struct archive_write_disk *a) + return; + + /* As long as the path is too long... */ +- while (strlen(tail) > PATH_MAX) { ++ while (strlen(tail) >= PATH_MAX) { + /* Locate a dir prefix shorter than PATH_MAX. */ + tail += PATH_MAX - 8; + while (tail > a->name && *tail != '/') +@@ -1996,6 +1997,10 @@ create_filesystem_object(struct archive_write_disk *a) + const char *linkname; + mode_t final_mode, mode; + int r; ++ /* these for check_path_for_symlinks */ ++ char *linkname_copy; /* non-const copy of linkname */ ++ struct archive_string error_string; ++ int error_number; + + /* We identify hard/symlinks according to the link names. */ + /* Since link(2) and symlink(2) don't handle modes, we're done here. */ +@@ -2004,6 +2009,18 @@ create_filesystem_object(struct archive_write_disk *a) + #if !HAVE_LINK + return (EPERM); + #else ++ archive_string_init(&error_string); ++ linkname_copy = strdup(linkname); ++ if (linkname_copy == NULL) { ++ return (EPERM); ++ } ++ r = check_path_for_symlinks(linkname_copy, &error_number, &error_string, a->flags); ++ free(linkname_copy); ++ if (r != ARCHIVE_OK) { ++ archive_set_error(&a->archive, error_number, "%s", error_string.s); ++ /* EPERM is more appropriate than error_number for our callers */ ++ return (EPERM); ++ } + r = link(linkname, a->name) ? errno : 0; + /* + * New cpio and pax formats allow hardlink entries +@@ -2343,99 +2360,214 @@ current_fixup(struct archive_write_disk *a, const char *pathname) + * recent paths. + */ + /* TODO: Extend this to support symlinks on Windows Vista and later. */ +-static int +-check_symlinks(struct archive_write_disk *a) ++ ++/* ++ * Checks the given path to see if any elements along it are symlinks. Returns ++ * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. ++ */ ++static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags) + { + #if !defined(HAVE_LSTAT) + /* Platform doesn't have lstat, so we can't look for symlinks. */ +- (void)a; /* UNUSED */ ++ (void)path; /* UNUSED */ ++ (void)error_number; /* UNUSED */ ++ (void)error_string; /* UNUSED */ ++ (void)flags; /* UNUSED */ + return (ARCHIVE_OK); + #else +- char *pn; ++ int res = ARCHIVE_OK; ++ char *tail; ++ char *head; ++ int last; + char c; + int r; + struct stat st; ++ int restore_pwd; ++ ++ /* Nothing to do here if name is empty */ ++ if(path[0] == '\0') ++ return (ARCHIVE_OK); + + /* + * Guard against symlink tricks. Reject any archive entry whose + * destination would be altered by a symlink. ++ * ++ * Walk the filename in chunks separated by '/'. For each segment: ++ * - if it doesn't exist, continue ++ * - if it's symlink, abort or remove it ++ * - if it's a directory and it's not the last chunk, cd into it ++ * As we go: ++ * head points to the current (relative) path ++ * tail points to the temporary \0 terminating the segment we're currently examining ++ * c holds what used to be in *tail ++ * last is 1 if this is the last tail + */ +- /* Whatever we checked last time doesn't need to be re-checked. */ +- pn = a->name; +- if (archive_strlen(&(a->path_safe)) > 0) { +- char *p = a->path_safe.s; +- while ((*pn != '\0') && (*p == *pn)) +- ++p, ++pn; +- } +- c = pn[0]; +- /* Keep going until we've checked the entire name. */ +- while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) { ++ restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); ++ __archive_ensure_cloexec_flag(restore_pwd); ++ if (restore_pwd < 0) ++ return (ARCHIVE_FATAL); ++ head = path; ++ tail = path; ++ last = 0; ++ ++ /* TODO: reintroduce a safe cache here? */ ++ ++ /* Keep going until we've checked the entire name. ++ * head, tail, path all alias the same string, which is ++ * temporarily zeroed at tail, so be careful restoring the ++ * stashed (c=tail[0]) for error messages. ++ * Exiting the loop with break is okay; continue is not. ++ */ ++ while (!last) { ++ /* Skip the separator we just consumed, plus any adjacent ones */ ++ while (*tail == '/') ++ ++tail; + /* Skip the next path element. */ +- while (*pn != '\0' && *pn != '/') +- ++pn; +- c = pn[0]; +- pn[0] = '\0'; ++ while (*tail != '\0' && *tail != '/') ++ ++tail; ++ /* is this the last path component? */ ++ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0'); ++ /* temporarily truncate the string here */ ++ c = tail[0]; ++ tail[0] = '\0'; + /* Check that we haven't hit a symlink. */ +- r = lstat(a->name, &st); ++ r = lstat(head, &st); + if (r != 0) { ++ tail[0] = c; + /* We've hit a dir that doesn't exist; stop now. */ + if (errno == ENOENT) + break; ++ /* Treat any other error as fatal - best to be paranoid here */ ++ if(error_number) *error_number = errno; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "Could not stat %s", ++ path); ++ res = (ARCHIVE_FATAL); ++ break; ++ } else if (S_ISDIR(st.st_mode)) { ++ if (!last) { ++ if (chdir(head) != 0) { ++ tail[0] = c; ++ if(error_number) *error_number = errno; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "Could not chdir %s", ++ path); ++ res = (ARCHIVE_FATAL); ++ break; ++ } ++ /* Our view is now from inside this dir: */ ++ head = tail + 1; ++ } + } else if (S_ISLNK(st.st_mode)) { +- if (c == '\0') { ++ if (last) { + /* + * Last element is symlink; remove it + * so we can overwrite it with the + * item being extracted. + */ +- if (unlink(a->name)) { +- archive_set_error(&a->archive, errno, +- "Could not remove symlink %s", +- a->name); +- pn[0] = c; +- return (ARCHIVE_FAILED); ++ if (unlink(head)) { ++ tail[0] = c; ++ if(error_number) *error_number = errno; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "Could not remove symlink %s", ++ path); ++ res = (ARCHIVE_FAILED); ++ break; + } +- a->pst = NULL; + /* + * Even if we did remove it, a warning + * is in order. The warning is silly, + * though, if we're just replacing one + * symlink with another symlink. + */ ++ tail[0] = c; ++ /* FIXME: not sure how important this is to restore + if (!S_ISLNK(a->mode)) { +- archive_set_error(&a->archive, 0, +- "Removing symlink %s", +- a->name); ++ if(error_number) *error_number = 0; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "Removing symlink %s", ++ path); + } ++ */ + /* Symlink gone. No more problem! */ +- pn[0] = c; +- return (0); +- } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { ++ res = (ARCHIVE_OK); ++ break; ++ } else if (flags & ARCHIVE_EXTRACT_UNLINK) { + /* User asked us to remove problems. */ +- if (unlink(a->name) != 0) { +- archive_set_error(&a->archive, 0, +- "Cannot remove intervening symlink %s", +- a->name); +- pn[0] = c; +- return (ARCHIVE_FAILED); ++ if (unlink(head) != 0) { ++ tail[0] = c; ++ if(error_number) *error_number = 0; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "Cannot remove intervening symlink %s", ++ path); ++ res = (ARCHIVE_FAILED); ++ break; + } +- a->pst = NULL; + } else { +- archive_set_error(&a->archive, 0, +- "Cannot extract through symlink %s", +- a->name); +- pn[0] = c; +- return (ARCHIVE_FAILED); ++ tail[0] = c; ++ if(error_number) *error_number = 0; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "Cannot extract through symlink %s", ++ path); ++ res = (ARCHIVE_FAILED); ++ break; + } + } ++ /* be sure to always maintain this */ ++ tail[0] = c; + } +- pn[0] = c; +- /* We've checked and/or cleaned the whole path, so remember it. */ +- archive_strcpy(&a->path_safe, a->name); +- return (ARCHIVE_OK); ++ /* Catches loop exits via break */ ++ tail[0] = c; ++#ifdef HAVE_FCHDIR ++ /* If we changed directory above, restore it here. */ ++ if (restore_pwd >= 0) { ++ r = fchdir(restore_pwd); ++ if (r != 0) { ++ if(error_number) *error_number = 0; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "Cannot extract through symlink %s", ++ path); ++ } ++ close(restore_pwd); ++ restore_pwd = -1; ++ if (r != 0) { ++ res = (ARCHIVE_FATAL); ++ } ++ } ++#endif ++ /* TODO: reintroduce a safe cache here? */ ++ return res; + #endif + } + ++/* ++ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise ++ * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED} ++ */ ++static int ++check_symlinks(struct archive_write_disk *a) ++{ ++ struct archive_string error_string; ++ int error_number; ++ int rc; ++ archive_string_init(&error_string); ++ rc = check_path_for_symlinks(a->name, &error_number, &error_string, a->flags); ++ if (rc != ARCHIVE_OK) { ++ archive_set_error(&a->archive, error_number, "%s", error_string.s); ++ } ++ archive_string_free(&error_string); ++ a->pst = NULL; /* to be safe */ ++ return rc; ++} ++ ++ + #if defined(__CYGWIN__) + /* + * 1. Convert a path separator from '\' to '/' . diff --git a/SOURCES/libarchive-3.1.2-CVE-2016-5844.patch b/SOURCES/libarchive-3.1.2-CVE-2016-5844.patch new file mode 100644 index 0000000..d769af5 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2016-5844.patch @@ -0,0 +1,40 @@ +From 3ad08e01b4d253c66ae56414886089684155af22 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sun, 19 Jun 2016 14:34:37 -0700 +Subject: [PATCH] Issue 717: Fix integer overflow when computing location of + volume descriptor + +The multiplication here defaulted to 'int' but calculations +of file positions should always use int64_t. A simple cast +suffices to fix this since the base location is always 32 bits +for ISO, so multiplying by the sector size will never overflow +a 64-bit integer. +--- + libarchive/archive_read_support_format_iso9660.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c +index 6934cee..f41ba38 100644 +--- a/libarchive/archive_read_support_format_iso9660.c ++++ b/libarchive/archive_read_support_format_iso9660.c +@@ -1091,7 +1091,7 @@ choose_volume(struct archive_read *a, struct iso9660 *iso9660) + /* This condition is unlikely; by way of caution. */ + vd = &(iso9660->joliet); + +- skipsize = LOGICAL_BLOCK_SIZE * vd->location; ++ skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location; + skipsize = __archive_read_consume(a, skipsize); + if (skipsize < 0) + return ((int)skipsize); +@@ -1129,7 +1129,7 @@ choose_volume(struct archive_read *a, struct iso9660 *iso9660) + && iso9660->seenJoliet) { + /* Switch reading data from primary to joliet. */ + vd = &(iso9660->joliet); +- skipsize = LOGICAL_BLOCK_SIZE * vd->location; ++ skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location; + skipsize -= iso9660->current_position; + skipsize = __archive_read_consume(a, skipsize); + if (skipsize < 0) +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2017-14503.patch b/SOURCES/libarchive-3.1.2-CVE-2017-14503.patch new file mode 100644 index 0000000..deebbed --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2017-14503.patch @@ -0,0 +1,29 @@ +From 2c8c83b9731ff822fad6cc8c670ea5519c366a14 Mon Sep 17 00:00:00 2001 +From: Joerg Sonnenberger +Date: Thu, 19 Jul 2018 21:14:53 +0200 +Subject: [PATCH] Reject LHA archive entries with negative size. + +--- + libarchive/archive_read_support_format_lha.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c +index b8ef4ae1..95c99bb1 100644 +--- a/libarchive/archive_read_support_format_lha.c ++++ b/libarchive/archive_read_support_format_lha.c +@@ -701,6 +701,12 @@ archive_read_format_lha_read_header(struct archive_read *a, + * Prepare variables used to read a file content. + */ + lha->entry_bytes_remaining = lha->compsize; ++ if (lha->entry_bytes_remaining < 0) { ++ archive_set_error(&a->archive, ++ ARCHIVE_ERRNO_FILE_FORMAT, ++ "Invalid LHa entry size"); ++ return (ARCHIVE_FATAL); ++ } + lha->entry_offset = 0; + lha->entry_crc_calculated = 0; + +-- +2.20.1 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2019-1000019.patch b/SOURCES/libarchive-3.1.2-CVE-2019-1000019.patch new file mode 100644 index 0000000..f05595e --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2019-1000019.patch @@ -0,0 +1,58 @@ +From 65a23f5dbee4497064e9bb467f81138a62b0dae1 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Tue, 1 Jan 2019 16:01:40 +1100 +Subject: [PATCH 2/2] 7zip: fix crash when parsing certain archives + +Fuzzing with CRCs disabled revealed that a call to get_uncompressed_data() +would sometimes fail to return at least 'minimum' bytes. This can cause +the crc32() invocation in header_bytes to read off into invalid memory. + +A specially crafted archive can use this to cause a crash. + +An ASAN trace is below, but ASAN is not required - an uninstrumented +binary will also crash. + +==7719==ERROR: AddressSanitizer: SEGV on unknown address 0x631000040000 (pc 0x7fbdb3b3ec1d bp 0x7ffe77a51310 sp 0x7ffe77a51150 T0) +==7719==The signal is caused by a READ memory access. + #0 0x7fbdb3b3ec1c in crc32_z (/lib/x86_64-linux-gnu/libz.so.1+0x2c1c) + #1 0x84f5eb in header_bytes (/tmp/libarchive/bsdtar+0x84f5eb) + #2 0x856156 in read_Header (/tmp/libarchive/bsdtar+0x856156) + #3 0x84e134 in slurp_central_directory (/tmp/libarchive/bsdtar+0x84e134) + #4 0x849690 in archive_read_format_7zip_read_header (/tmp/libarchive/bsdtar+0x849690) + #5 0x5713b7 in _archive_read_next_header2 (/tmp/libarchive/bsdtar+0x5713b7) + #6 0x570e63 in _archive_read_next_header (/tmp/libarchive/bsdtar+0x570e63) + #7 0x6f08bd in archive_read_next_header (/tmp/libarchive/bsdtar+0x6f08bd) + #8 0x52373f in read_archive (/tmp/libarchive/bsdtar+0x52373f) + #9 0x5257be in tar_mode_x (/tmp/libarchive/bsdtar+0x5257be) + #10 0x51daeb in main (/tmp/libarchive/bsdtar+0x51daeb) + #11 0x7fbdb27cab96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310 + #12 0x41dd09 in _start (/tmp/libarchive/bsdtar+0x41dd09) + +This was primarly done with afl and FairFuzz. Some early corpus entries +may have been generated by qsym. +--- + libarchive/archive_read_support_format_7zip.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c +index bccbf896..b6d1505d 100644 +--- a/libarchive/archive_read_support_format_7zip.c ++++ b/libarchive/archive_read_support_format_7zip.c +@@ -2964,13 +2964,7 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, + if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { + /* Copy mode. */ + +- /* +- * Note: '1' here is a performance optimization. +- * Recall that the decompression layer returns a count of +- * available bytes; asking for more than that forces the +- * decompressor to combine reads by copying data. +- */ +- *buff = __archive_read_ahead(a, 1, &bytes_avail); ++ *buff = __archive_read_ahead(a, minimum, &bytes_avail); + if (bytes_avail <= 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, +-- +2.20.1 + diff --git a/SOURCES/libarchive-3.1.2-CVE-2019-1000020.patch b/SOURCES/libarchive-3.1.2-CVE-2019-1000020.patch new file mode 100644 index 0000000..b314520 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-CVE-2019-1000020.patch @@ -0,0 +1,59 @@ +From 8312eaa576014cd9b965012af51bc1f967b12423 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Tue, 1 Jan 2019 17:10:49 +1100 +Subject: [PATCH 1/2] iso9660: Fail when expected Rockridge extensions is + missing + +A corrupted or malicious ISO9660 image can cause read_CE() to loop +forever. + +read_CE() calls parse_rockridge(), expecting a Rockridge extension +to be read. However, parse_rockridge() is structured as a while +loop starting with a sanity check, and if the sanity check fails +before the loop has run, the function returns ARCHIVE_OK without +advancing the position in the file. This causes read_CE() to retry +indefinitely. + +Make parse_rockridge() return ARCHIVE_WARN if it didn't read an +extension. As someone with no real knowledge of the format, this +seems more apt than ARCHIVE_FATAL, but both the call-sites escalate +it to a fatal error immediately anyway. + +Found with a combination of AFL, afl-rb (FairFuzz) and qsym. +--- + libarchive/archive_read_support_format_iso9660.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c +index 28acfefb..bad8f1df 100644 +--- a/libarchive/archive_read_support_format_iso9660.c ++++ b/libarchive/archive_read_support_format_iso9660.c +@@ -2102,6 +2102,7 @@ parse_rockridge(struct archive_read *a, struct file_info *file, + const unsigned char *p, const unsigned char *end) + { + struct iso9660 *iso9660; ++ int entry_seen = 0; + + iso9660 = (struct iso9660 *)(a->format->data); + +@@ -2257,8 +2258,16 @@ parse_rockridge(struct archive_read *a, struct file_info *file, + } + + p += p[2]; ++ entry_seen = 1; ++ } ++ ++ if (entry_seen) ++ return (ARCHIVE_OK); ++ else { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Tried to parse Rockridge extensions, but none found"); ++ return (ARCHIVE_WARN); + } +- return (ARCHIVE_OK); + } + + static int +-- +2.20.1 + diff --git a/SOURCES/libarchive-3.1.2-rhbz-1347085.patch b/SOURCES/libarchive-3.1.2-rhbz-1347085.patch new file mode 100644 index 0000000..c60280b --- /dev/null +++ b/SOURCES/libarchive-3.1.2-rhbz-1347085.patch @@ -0,0 +1,84 @@ +From 3014e19820ea53c15c90f9d447ca3e668a0b76c6 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 28 May 2016 11:50:39 -0700 +Subject: [PATCH] Issue 711: Be more careful about verifying filename lengths + when writing ISO9660 archives + +* Don't cast size_t to int, since this can lead to overflow + on machines where sizeof(int) < sizeof(size_t) +* Check a + b > limit by writing it as + a > limit || b > limit || a + b > limit + to avoid problems when a + b wraps around. +--- + libarchive/archive_write_set_format_iso9660.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c +index 4d832fb..cb3e54e 100644 +--- a/libarchive/archive_write_set_format_iso9660.c ++++ b/libarchive/archive_write_set_format_iso9660.c +@@ -6225,7 +6225,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, + unsigned char *p; + size_t l; + int r; +- int ffmax, parent_len; ++ size_t ffmax, parent_len; + static const struct archive_rb_tree_ops rb_ops = { + isoent_cmp_node_joliet, isoent_cmp_key_joliet + }; +@@ -6239,7 +6239,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, + else + ffmax = 128; + +- r = idr_start(a, idr, isoent->children.cnt, ffmax, 6, 2, &rb_ops); ++ r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops); + if (r < 0) + return (r); + +@@ -6252,7 +6252,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, + int ext_off, noff, weight; + size_t lt; + +- if ((int)(l = np->file->basename_utf16.length) > ffmax) ++ if ((l = np->file->basename_utf16.length) > ffmax) + l = ffmax; + + p = malloc((l+1)*2); +@@ -6285,7 +6285,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, + /* + * Get a length of MBS of a full-pathname. + */ +- if ((int)np->file->basename_utf16.length > ffmax) { ++ if (np->file->basename_utf16.length > ffmax) { + if (archive_strncpy_l(&iso9660->mbs, + (const char *)np->identifier, l, + iso9660->sconv_from_utf16be) != 0 && +@@ -6302,7 +6302,9 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, + + /* If a length of full-pathname is longer than 240 bytes, + * it violates Joliet extensions regulation. */ +- if (parent_len + np->mb_len > 240) { ++ if (parent_len > 240 ++ || np->mb_len > 240 ++ || parent_len + np->mb_len > 240) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "The regulation of Joliet extensions;" + " A length of a full-pathname of `%s' is " +@@ -6314,11 +6316,11 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, + + /* Make an offset of the number which is used to be set + * hexadecimal number to avoid duplicate identifier. */ +- if ((int)l == ffmax) ++ if (l == ffmax) + noff = ext_off - 6; +- else if ((int)l == ffmax-2) ++ else if (l == ffmax-2) + noff = ext_off - 4; +- else if ((int)l == ffmax-4) ++ else if (l == ffmax-4) + noff = ext_off - 2; + else + noff = ext_off; +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-rhbz-1347086.patch b/SOURCES/libarchive-3.1.2-rhbz-1347086.patch new file mode 100644 index 0000000..a9abbd5 --- /dev/null +++ b/SOURCES/libarchive-3.1.2-rhbz-1347086.patch @@ -0,0 +1,163 @@ +From 6e06b1c89dd0d16f74894eac4cfc1327a06ee4a0 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Sat, 10 Jan 2015 12:24:58 -0800 +Subject: [PATCH] Fix a potential crash issue discovered by Alexander + Cherepanov: + +It seems bsdtar automatically handles stacked compression. This is a +nice feature but it could be problematic when it's completely +unlimited. Most clearly it's illustrated with quines: + +$ curl -sRO http://www.maximumcompression.com/selfgz.gz +$ (ulimit -v 10000000 && bsdtar -tvf selfgz.gz) +bsdtar: Error opening archive: Can't allocate data for gzip decompression + +Without ulimit, bsdtar will eat all available memory. This could also +be a problem for other applications using libarchive. +--- + Makefile.am | 2 ++ + libarchive/archive_read.c | 7 ++-- + libarchive/test/CMakeLists.txt | 1 + + libarchive/test/test_read_too_many_filters.c | 45 ++++++++++++++++++++++++ + libarchive/test/test_read_too_many_filters.gz.uu | 15 ++++++++ + 5 files changed, 68 insertions(+), 2 deletions(-) + create mode 100644 libarchive/test/test_read_too_many_filters.c + create mode 100644 libarchive/test/test_read_too_many_filters.gz.uu + +diff --git a/Makefile.am b/Makefile.am +index 3fa2d22..d6e40a2 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -432,6 +432,7 @@ libarchive_test_SOURCES= \ + libarchive/test/test_read_pax_truncated.c \ + libarchive/test/test_read_position.c \ + libarchive/test/test_read_set_format.c \ ++ libarchive/test/test_read_too_many_filters.c \ + libarchive/test/test_read_truncated.c \ + libarchive/test/test_read_truncated_filter.c \ + libarchive/test/test_sparse_basic.c \ +@@ -693,6 +694,7 @@ libarchive_test_EXTRA_DIST=\ + libarchive/test/test_read_splitted_rar_ab.uu \ + libarchive/test/test_read_splitted_rar_ac.uu \ + libarchive/test/test_read_splitted_rar_ad.uu \ ++ libarchive/test/test_read_too_many_filters.gz.uu \ + libarchive/test/test_splitted_rar_seek_support_aa.uu \ + libarchive/test/test_splitted_rar_seek_support_ab.uu \ + libarchive/test/test_splitted_rar_seek_support_ac.uu \ +diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c +index 048c316..f8dde89 100644 +--- a/libarchive/archive_read.c ++++ b/libarchive/archive_read.c +@@ -544,13 +544,13 @@ archive_read_open1(struct archive *_a) + static int + choose_filters(struct archive_read *a) + { +- int number_bidders, i, bid, best_bid; ++ int number_bidders, i, bid, best_bid, n; + struct archive_read_filter_bidder *bidder, *best_bidder; + struct archive_read_filter *filter; + ssize_t avail; + int r; + +- for (;;) { ++ for (n = 0; n < 25; ++n) { + number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); + + best_bid = 0; +@@ -596,6 +596,9 @@ choose_filters(struct archive_read *a) + return (ARCHIVE_FATAL); + } + } ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Input requires too many filters for decoding"); ++ return (ARCHIVE_FATAL); + } + + /* +diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt +index d2eb2c2..6ac850d 100644 +--- a/libarchive/test/CMakeLists.txt ++++ b/libarchive/test/CMakeLists.txt +@@ -147,6 +147,7 @@ IF(ENABLE_TEST) + test_read_pax_truncated.c + test_read_position.c + test_read_set_format.c ++ test_read_too_many_filters.c + test_read_truncated.c + test_read_truncated_filter.c + test_sparse_basic.c +diff --git a/libarchive/test/test_read_too_many_filters.c b/libarchive/test/test_read_too_many_filters.c +new file mode 100644 +index 0000000..37cab24 +--- /dev/null ++++ b/libarchive/test/test_read_too_many_filters.c +@@ -0,0 +1,45 @@ ++/*- ++ * Copyright (c) 2003-2008,2015 Tim Kientzle ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "test.h" ++ ++DEFINE_TEST(test_read_too_many_filters) ++{ ++ const char *name = "test_read_too_many_filters.gz"; ++ struct archive *a; ++ int r; ++ ++ assert((a = archive_read_new()) != NULL); ++ r = archive_read_support_filter_gzip(a); ++ if (r == ARCHIVE_WARN) { ++ skipping("gzip reading not fully supported on this platform"); ++ } ++ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); ++ extract_reference_file(name); ++ assertEqualIntA(a, ARCHIVE_FATAL, ++ archive_read_open_filename(a, name, 200)); ++ ++ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); ++ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); ++} +diff --git a/libarchive/test/test_read_too_many_filters.gz.uu b/libarchive/test/test_read_too_many_filters.gz.uu +new file mode 100644 +index 0000000..6bf6614 +--- /dev/null ++++ b/libarchive/test/test_read_too_many_filters.gz.uu +@@ -0,0 +1,15 @@ ++This is a valid gzip file that decompresses to itself, from ++ http://www.maximumcompression.com/selfgz.gz ++ ++This is used in test_read_too_many_filters to try to ++crash libarchive by forcing it to spawn an unending ++list of gunzip filters. ++ ++begin 644 test_read_too_many_filters.gz ++M'XL(`````````P`/`/#_'XL(`````````P`/`/#_````__\```#__X)QH5P` ++M`!X`X?\```#__P```/__@G&A7```'@#A_P```/__````__\```#__P```/__ ++M````__\```#__\(FAF`!`!0`Z_\```#__P```/__PB:&8`$`%`#K_\(FAF`! ++M`!0`Z_^9(#6-B"@Q,C,T`K/`+```%`#K_P*SP"P``!0`Z_]"B"'$`````/__ ++>`P!#2DTAT@```$*((<0`````__\#`$-*32'2```` ++` ++end +-- +2.7.4 + diff --git a/SOURCES/libarchive-3.1.2-testsuite.patch b/SOURCES/libarchive-3.1.2-testsuite.patch new file mode 100644 index 0000000..8a9841c --- /dev/null +++ b/SOURCES/libarchive-3.1.2-testsuite.patch @@ -0,0 +1,150 @@ +diff --git a/cpio/test/test_extract_cpio_lzo.c b/cpio/test/test_extract_cpio_lzo.c +index f351ba7..99476af 100644 +--- a/cpio/test/test_extract_cpio_lzo.c ++++ b/cpio/test/test_extract_cpio_lzo.c +@@ -27,7 +27,7 @@ __FBSDID("$FreeBSD$"); + + DEFINE_TEST(test_extract_cpio_lzo) + { +- const char *reffile = "test_extract.cpio.lrz"; ++ const char *reffile = "test_extract.cpio.lzo"; + int f; + + extract_reference_file(reffile); +diff --git a/libarchive/test/test_write_filter_lzop.c b/libarchive/test/test_write_filter_lzop.c +index 9e840bd..a32932c 100644 +--- a/libarchive/test/test_write_filter_lzop.c ++++ b/libarchive/test/test_write_filter_lzop.c +@@ -39,7 +39,7 @@ DEFINE_TEST(test_write_filter_lzop) + size_t buffsize, datasize; + char path[16]; + size_t used1, used2; +- int i, r, use_prog = 0; ++ int i, r, use_prog = 0, filecount; + + assert((a = archive_write_new()) != NULL); + r = archive_write_add_filter_lzop(a); +@@ -58,9 +58,10 @@ DEFINE_TEST(test_write_filter_lzop) + + datasize = 10000; + assert(NULL != (data = (char *)calloc(1, datasize))); ++ filecount = 10; + + /* +- * Write a 100 files and read them all back. ++ * Write a filecount files and read them all back. + */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); +@@ -77,7 +78,7 @@ DEFINE_TEST(test_write_filter_lzop) + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_filetype(ae, AE_IFREG); + archive_entry_set_size(ae, datasize); +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < filecount; i++) { + sprintf(path, "file%03d", i); + archive_entry_copy_pathname(ae, path); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); +@@ -97,7 +98,7 @@ DEFINE_TEST(test_write_filter_lzop) + } else { + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buff, used1)); +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < filecount; i++) { + sprintf(path, "file%03d", i); + if (!assertEqualInt(ARCHIVE_OK, + archive_read_next_header(a, &ae))) +@@ -133,7 +134,7 @@ DEFINE_TEST(test_write_filter_lzop) + archive_write_set_options(a, "lzop:compression-level=9")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used2)); +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < filecount; i++) { + sprintf(path, "file%03d", i); + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, path); +@@ -161,7 +162,7 @@ DEFINE_TEST(test_write_filter_lzop) + archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buff, used2)); +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < filecount; i++) { + sprintf(path, "file%03d", i); + if (!assertEqualInt(ARCHIVE_OK, + archive_read_next_header(a, &ae))) +@@ -186,7 +187,7 @@ DEFINE_TEST(test_write_filter_lzop) + archive_write_set_filter_option(a, NULL, "compression-level", "1")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used2)); +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < filecount; i++) { + sprintf(path, "file%03d", i); + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, path); +@@ -216,7 +217,7 @@ DEFINE_TEST(test_write_filter_lzop) + } else { + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buff, used2)); +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < filecount; i++) { + sprintf(path, "file%03d", i); + if (!assertEqualInt(ARCHIVE_OK, + archive_read_next_header(a, &ae))) +diff --git a/tar/test/test_option_b.c b/tar/test/test_option_b.c +index be2ae65..7164d4c 100644 +--- a/tar/test/test_option_b.c ++++ b/tar/test/test_option_b.c +@@ -25,8 +25,14 @@ + #include "test.h" + __FBSDID("$FreeBSD$"); + ++static char *ustar_opt = " --format=ustar"; ++ + DEFINE_TEST(test_option_b) + { ++ char *testprog_ustar = malloc(strlen(testprog) + strlen(ustar_opt) + 2); ++ strcpy(testprog_ustar, testprog); ++ strcat(testprog_ustar, ustar_opt); ++ + assertMakeFile("file1", 0644, "file1"); + if (systemf("cat file1 > test_cat.out 2> test_cat.err") != 0) { + skipping("Platform doesn't have cat"); +@@ -36,7 +42,7 @@ DEFINE_TEST(test_option_b) + /* + * Bsdtar does not pad if the output is going directly to a disk file. + */ +- assertEqualInt(0, systemf("%s -cf archive1.tar file1 >test1.out 2>test1.err", testprog)); ++ assertEqualInt(0, systemf("%s -cf archive1.tar file1 >test1.out 2>test1.err", testprog_ustar)); + failure("bsdtar does not pad archives written directly to regular files"); + assertFileSize("archive1.tar", 2048); + assertEmptyFile("test1.out"); +@@ -46,24 +52,24 @@ DEFINE_TEST(test_option_b) + * Bsdtar does pad to the block size if the output is going to a socket. + */ + /* Default is -b 20 */ +- assertEqualInt(0, systemf("%s -cf - file1 2>test2.err | cat >archive2.tar ", testprog)); ++ assertEqualInt(0, systemf("%s -cf - file1 2>test2.err | cat >archive2.tar ", testprog_ustar)); + failure("bsdtar does pad archives written to pipes"); + assertFileSize("archive2.tar", 10240); + assertEmptyFile("test2.err"); + +- assertEqualInt(0, systemf("%s -cf - -b 20 file1 2>test3.err | cat >archive3.tar ", testprog)); ++ assertEqualInt(0, systemf("%s -cf - -b 20 file1 2>test3.err | cat >archive3.tar ", testprog_ustar)); + assertFileSize("archive3.tar", 10240); + assertEmptyFile("test3.err"); + +- assertEqualInt(0, systemf("%s -cf - -b 10 file1 2>test4.err | cat >archive4.tar ", testprog)); ++ assertEqualInt(0, systemf("%s -cf - -b 10 file1 2>test4.err | cat >archive4.tar ", testprog_ustar)); + assertFileSize("archive4.tar", 5120); + assertEmptyFile("test4.err"); + +- assertEqualInt(0, systemf("%s -cf - -b 1 file1 2>test5.err | cat >archive5.tar ", testprog)); ++ assertEqualInt(0, systemf("%s -cf - -b 1 file1 2>test5.err | cat >archive5.tar ", testprog_ustar)); + assertFileSize("archive5.tar", 2048); + assertEmptyFile("test5.err"); + +- assertEqualInt(0, systemf("%s -cf - -b 8192 file1 2>test6.err | cat >archive6.tar ", testprog)); ++ assertEqualInt(0, systemf("%s -cf - -b 8192 file1 2>test6.err | cat >archive6.tar ", testprog_ustar)); + assertFileSize("archive6.tar", 4194304); + assertEmptyFile("test6.err"); + diff --git a/SOURCES/libarchive-3.1.3-CVE-2013-0211_read_buffer_overflow.patch b/SOURCES/libarchive-3.1.3-CVE-2013-0211_read_buffer_overflow.patch new file mode 100644 index 0000000..78427ce --- /dev/null +++ b/SOURCES/libarchive-3.1.3-CVE-2013-0211_read_buffer_overflow.patch @@ -0,0 +1,32 @@ +From 22531545514043e04633e1c015c7540b9de9dbe4 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +Date: Fri, 22 Mar 2013 23:48:41 -0700 +Subject: [PATCH] Limit write requests to at most INT_MAX. This prevents a + certain common programming error (passing -1 to write) from leading to other + problems deeper in the library. + +--- + libarchive/archive_write.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c +index eede5e0..be85621 100644 +--- a/libarchive/archive_write.c ++++ b/libarchive/archive_write.c +@@ -673,8 +673,13 @@ static ssize_t + _archive_write_data(struct archive *_a, const void *buff, size_t s) + { + struct archive_write *a = (struct archive_write *)_a; ++ const size_t max_write = INT_MAX; ++ + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_DATA, "archive_write_data"); ++ /* In particular, this catches attempts to pass negative values. */ ++ if (s > max_write) ++ s = max_write; + archive_clear_error(&a->archive); + return ((a->format_write_data)(a, buff, s)); + } +-- +1.8.1 + diff --git a/SOURCES/libarchive-3.3.2-CVE-2018-1000877.patch b/SOURCES/libarchive-3.3.2-CVE-2018-1000877.patch new file mode 100644 index 0000000..e980aa6 --- /dev/null +++ b/SOURCES/libarchive-3.3.2-CVE-2018-1000877.patch @@ -0,0 +1,34 @@ +From 88311f46cdfc719d26bb99d3b47944eb92ceae02 Mon Sep 17 00:00:00 2001 +From: Ondrej Dubaj +Date: Tue, 30 Apr 2019 11:50:33 +0200 +Subject: [PATCH] Avoid a double-free when a window size of 0 is specified + +new_size can be 0 with a malicious or corrupted RAR archive. + +realloc(area, 0) is equivalent to free(area), so the region would +be free()d here and the free()d again in the cleanup function. + +Found with a setup running AFL, afl-rb, and qsym. +--- + libarchive/archive_read_support_format_rar.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c +index c4a8278..3f88eef 100644 +--- a/libarchive/archive_read_support_format_rar.c ++++ b/libarchive/archive_read_support_format_rar.c +@@ -2317,6 +2317,11 @@ parse_codes(struct archive_read *a) + new_size = DICTIONARY_MAX_SIZE; + else + new_size = rar_fls((unsigned int)rar->unp_size) << 1; ++ if (new_size == 0) { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Zero window size is invalid."); ++ return (ARCHIVE_FATAL); ++ } + new_window = realloc(rar->lzss.window, new_size); + if (new_window == NULL) { + archive_set_error(&a->archive, ENOMEM, +-- +2.17.1 + diff --git a/SOURCES/libarchive-3.3.2-CVE-2018-1000878.patch b/SOURCES/libarchive-3.3.2-CVE-2018-1000878.patch new file mode 100644 index 0000000..2736827 --- /dev/null +++ b/SOURCES/libarchive-3.3.2-CVE-2018-1000878.patch @@ -0,0 +1,75 @@ +From d00ccaf8c20efbd009964e3e2697d26907d14163 Mon Sep 17 00:00:00 2001 +From: Ondrej Dubaj +Date: Tue, 30 Apr 2019 11:36:08 +0200 +Subject: [PATCH] rar: file split across multi-part archives must match + +Fuzzing uncovered some UAF and memory overrun bugs where a file in a +single file archive reported that it was split across multiple +volumes. This was caused by ppmd7 operations calling +rar_br_fillup. This would invoke rar_read_ahead, which would in some +situations invoke archive_read_format_rar_read_header. That would +check the new file name against the old file name, and if they didn't +match up it would free the ppmd7 buffer and allocate a new +one. However, because the ppmd7 decoder wasn't actually done with the +buffer, it would continue to used the freed buffer. Both reads and +writes to the freed region can be observed. + +This is quite tricky to solve: once the buffer has been freed it is +too late, as the ppmd7 decoder functions almost universally assume +success - there's no way for ppmd_read to signal error, nor are there +good ways for functions like Range_Normalise to propagate them. So we +can't detect after the fact that we're in an invalid state - e.g. by +checking rar->cursor, we have to prevent ourselves from ever ending up +there. So, when we are in the dangerous part or rar_read_ahead that +assumes a valid split, we set a flag force read_header to either go +down the path for split files or bail. This means that the ppmd7 +decoder keeps a valid buffer and just runs out of data. + +Found with a combination of AFL, afl-rb and qsym. +--- + libarchive/archive_read_support_format_rar.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c +index cbb14c3..c4a8278 100644 +--- a/libarchive/archive_read_support_format_rar.c ++++ b/libarchive/archive_read_support_format_rar.c +@@ -258,6 +258,7 @@ struct rar + struct data_block_offsets *dbo; + unsigned int cursor; + unsigned int nodes; ++ char filename_must_match; + + /* LZSS members */ + struct huffman_code maincode; +@@ -1570,6 +1571,12 @@ read_header(struct archive_read *a, struct archive_entry *entry, + } + return ret; + } ++ else if (rar->filename_must_match) ++ { ++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, ++ "Mismatch of file parts split across multi-volume archive"); ++ return (ARCHIVE_FATAL); ++ } + + rar->filename_save = (char*)realloc(rar->filename_save, + filename_size + 1); +@@ -2938,12 +2945,14 @@ rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) + else if (*avail == 0 && rar->main_flags & MHD_VOLUME && + rar->file_flags & FHD_SPLIT_AFTER) + { ++ rar->filename_must_match = 1; + ret = archive_read_format_rar_read_header(a, a->entry); + if (ret == (ARCHIVE_EOF)) + { + rar->has_endarc_header = 1; + ret = archive_read_format_rar_read_header(a, a->entry); + } ++ rar->filename_must_match = 0; + if (ret != (ARCHIVE_OK)) + return NULL; + return rar_read_ahead(a, min, avail); +-- +2.17.1 + diff --git a/SPECS/libarchive.spec b/SPECS/libarchive.spec new file mode 100644 index 0000000..608e3c3 --- /dev/null +++ b/SPECS/libarchive.spec @@ -0,0 +1,375 @@ +Name: libarchive +Version: 3.1.2 +Release: 12%{?dist} +Summary: A library for handling streaming archive formats + +Group: System Environment/Libraries +License: BSD +URL: http://www.libarchive.org/ +Source0: http://www.libarchive.org/downloads/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + + +BuildRequires: bison +BuildRequires: sharutils +BuildRequires: zlib-devel +BuildRequires: bzip2-devel +BuildRequires: xz-devel +BuildRequires: lzo-devel +BuildRequires: e2fsprogs-devel +BuildRequires: libacl-devel +BuildRequires: libattr-devel +BuildRequires: openssl-devel +BuildRequires: libxml2-devel +BuildRequires: automake autoconf libtool + + +# CVE-2013-0211 libarchive: read buffer overflow on 64-bit systems +# https://bugzilla.redhat.com/show_bug.cgi?id=927105 +Patch0: libarchive-3.1.3-CVE-2013-0211_read_buffer_overflow.patch + +Patch1: libarchive-3.1.2-testsuite.patch + +# A bunch of security patches from 2016 summer +Patch2: libarchive-3.1.2-rhbz-1347085.patch +Patch3: libarchive-3.1.2-rhbz-1347086.patch +Patch4: libarchive-3.1.2-CVE-2015-8916-CVE-2015-8917.patch +Patch5: libarchive-3.1.2-CVE-2015-8919.patch +Patch6: libarchive-3.1.2-CVE-2015-8920.patch +Patch7: libarchive-3.1.2-CVE-2015-8921.patch +Patch8: libarchive-3.1.2-CVE-2015-8922.patch +Patch9: libarchive-3.1.2-CVE-2015-8923.patch +Patch10: libarchive-3.1.2-CVE-2015-8924.patch +Patch11: libarchive-3.1.2-CVE-2015-8925.patch +Patch12: libarchive-3.1.2-CVE-2015-8926.patch +Patch13: libarchive-3.1.2-CVE-2015-8928.patch +Patch14: libarchive-3.1.2-CVE-2015-8930.patch +Patch15: libarchive-3.1.2-CVE-2015-8931.patch +Patch16: libarchive-3.1.2-CVE-2015-8932.patch +Patch17: libarchive-3.1.2-CVE-2015-8934.patch +Patch18: libarchive-3.1.2-CVE-2016-4300.patch +Patch19: libarchive-3.1.2-CVE-2016-4302.patch +Patch20: libarchive-3.1.2-CVE-2016-4809.patch +Patch21: libarchive-3.1.2-CVE-2016-5844.patch +Patch22: libarchive-3.1.2-CVE-2016-1541.patch +Patch23: libarchive-3.1.2-CVE-2016-5418.patch +Patch24: libarchive-3.1.2-CVE-2016-5418-variation.patch +Patch25: libarchive-3.1.2-CVE-2017-14503.patch +Patch26: libarchive-3.1.2-CVE-2019-1000019.patch +Patch27: libarchive-3.1.2-CVE-2019-1000020.patch +Patch28: libarchive-3.3.2-CVE-2018-1000878.patch +Patch29: libarchive-3.3.2-CVE-2018-1000877.patch + +%description +Libarchive is a programming library that can create and read several different +streaming archive formats, including most popular tar variants, several cpio +formats, and both BSD and GNU ar variants. It can also write shar archives and +read ISO9660 CDROM images and ZIP archives. + +%package devel +Summary: Development files for %{name} +Group: Development/Libraries +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +The %{name}-devel package contains libraries and header files for +developing applications that use %{name}. + + +%package -n bsdtar +Summary: Manipulate tape archives +Group: Applications/File +Requires: %{name} = %{version}-%{release} + +%description -n bsdtar +The bsdtar package contains standalone bsdtar utility split off regular +libarchive packages. + + +%package -n bsdcpio +Summary: Copy files to and from archives +Group: Applications/File +Requires: %{name} = %{version}-%{release} + +%description -n bsdcpio +The bsdcpio package contains standalone bsdcpio utility split off regular +libarchive packages. + +%global _hardened_build 1 + +%prep +%setup -q -n %{name}-%{version} +%patch0 -p1 -b .CVE-2013-0211 +# fix bugs in testsuite +# ~> upstream ~> 26629c191a & b539b2e597 & 9caa49246 +%patch1 -p1 -b .fix-testsuite + +%patch2 -p1 -b .rhbz-1347085 +%patch3 -p1 -b .rhbz-1347086 +%patch4 -p1 -b .CVE-2015-8916-CVE-2015-8917 +%patch5 -p1 -b .CVE-2015-8919 +%patch6 -p1 -b .CVE-2015-8920 +%patch7 -p1 -b .CVE-2015-8921 +%patch8 -p1 -b .CVE-2015-8922 +%patch9 -p1 -b .CVE-2015-8923 +%patch10 -p1 -b .CVE-2015-8924 +%patch11 -p1 -b .CVE-2015-8925 +%patch12 -p1 -b .CVE-2015-8926 +%patch13 -p1 -b .CVE-2015-8928 +%patch14 -p1 -b .CVE-2015-8930 +%patch15 -p1 -b .CVE-2015-8931 +%patch16 -p1 -b .CVE-2015-8932 +%patch17 -p1 -b .CVE-2015-8934 +%patch18 -p1 -b .CVE-2016-4300 +%patch19 -p1 -b .CVE-2016-4302 +%patch20 -p1 -b .CVE-2016-4809 +%patch21 -p1 -b .CVE-2016-5844 +%patch22 -p1 -b .CVE-2016-1541 +%patch23 -p1 -b .CVE-2016-5418 +%patch24 -p1 -b .CVE-2016-5418-var +%patch25 -p1 -b .CVE-2017-14503 +%patch26 -p1 -b .CVE-2019-1000019 +%patch27 -p1 -b .CVE-2019-1000020 +%patch28 -p1 -b .CVE-2019-1000878 +%patch29 -p1 -b .CVE-2019-1000877 + + +%build +build/autogen.sh +%configure --disable-static --disable-rpath +# remove rpaths +sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool +sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool + +test -z "$V" && verbose_make="V=1" +make %{?_smp_mflags} $verbose_make + + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT +find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' + + +%check +run_testsuite() +{ + LD_LIBRARY_PATH=`pwd`/.libs make check -j1 + res=$? + echo $res + if [ $res -ne 0 ]; then + # error happened - try to extract in koji as much info as possible + cat test-suite.log + echo "=========================" + err=`cat test-suite.log | grep "Details for failing tests" | cut -d: -f2` + for i in $err; do + find $i -printf "%p\n ~> a: %a\n ~> c: %c\n ~> t: %t\n ~> %s B\n" + echo "-------------------------" + cat $i/*.log + done + return 1 + else + find -name '*_test.log' -exec cat {} + + return 0 + fi +} + +# On a ppc/ppc64 is some race condition causing 'make check' fail on ppc +# when both 32 and 64 builds are done in parallel on the same machine in +# koji. Try to run once again if failed. +%ifarch ppc +run_testsuite || run_testsuite +%else +run_testsuite +%endif + + +%clean +rm -rf $RPM_BUILD_ROOT + + +%post -p /sbin/ldconfig + + +%postun -p /sbin/ldconfig + + +%files +%defattr(-,root,root,-) +%doc COPYING README NEWS +%{_libdir}/libarchive.so.13* +%{_mandir}/*/cpio.* +%{_mandir}/*/mtree.* +%{_mandir}/*/tar.* + +%files devel +%defattr(-,root,root,-) +%doc +%{_includedir}/*.h +%{_mandir}/*/archive* +%{_mandir}/*/libarchive* +%{_libdir}/libarchive.so +%{_libdir}/pkgconfig/libarchive.pc + +%files -n bsdtar +%defattr(-,root,root,-) +%doc COPYING README NEWS +%{_bindir}/bsdtar +%{_mandir}/*/bsdtar* + +%files -n bsdcpio +%defattr(-,root,root,-) +%doc COPYING README NEWS +%{_bindir}/bsdcpio +%{_mandir}/*/bsdcpio* + + +%changelog +* Tue Apr 30 2019 Ondrej Dubaj - 3.1.2-12 +- fixed use after free in RAR decoder (#1700749) +- fixed double free in RAR decoder (#1700748) + +* Fri Feb 22 2019 Pavel Raiskup - 3.1.2-11 +- fix out-of-bounds read within lha_read_data_none() (CVE-2017-14503) +- fix crash on crafted 7zip archives (CVE-2019-1000019) +- fix infinite loop in ISO9660 (CVE-2019-1000020) + +* Fri Aug 12 2016 Petr Kubat - 3.1.2-10 +- Fixes variation of CVE-2016-5418: Hard links could include ".." in their path. + +* Thu Aug 11 2016 Petr Kubat - 3.1.2-9 +- Fixes CVE-2016-5418: Archive Entry with type 1 (hardlink) causes file overwrite (#1365777) + +* Fri Jul 08 2016 Pavel Raiskup - 3.1.2-8 +- a bunch of security fixes (rhbz#1353065) + +* Fri Jan 24 2014 Daniel Mach - 3.1.2-7 +- Mass rebuild 2014-01-24 + +* Fri Dec 27 2013 Daniel Mach - 3.1.2-6 +- Mass rebuild 2013-12-27 + +* Mon Jul 22 2013 Pavel Raiskup - 3.1.2-5 +- try to workaround racy testsuite fail + +* Sun Jun 30 2013 Pavel Raiskup - 3.1.2-4 +- enable testsuite in the %%check phase + +* Mon Jun 24 2013 Pavel Raiskup - 3.1.2-3 +- bsdtar/bsdcpio should require versioned libarchive + +* Wed Apr 3 2013 Tomas Bzatek - 3.1.2-2 +- Remove libunistring-devel build require + +* Thu Mar 28 2013 Tomas Bzatek - 3.1.2-1 +- Update to 3.1.2 +- Fix CVE-2013-0211: read buffer overflow on 64-bit systems (#927105) + +* Thu Feb 14 2013 Fedora Release Engineering - 3.1.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Jan 14 2013 Tomas Bzatek - 3.1.1-1 +- Update to 3.1.1 +- NEWS seems to be valid UTF-8 nowadays + +* Wed Oct 03 2012 Pavel Raiskup - 3.0.4-3 +- better install manual pages for libarchive/bsdtar/bsdcpio (# ... ) +- several fedora-review fixes ...: +- Source0 has moved to github.com +- remove trailing white spaces +- repair summary to better describe bsdtar/cpiotar utilities + +* Thu Jul 19 2012 Fedora Release Engineering - 3.0.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Mon May 7 2012 Tomas Bzatek - 3.0.4-1 +- Update to 3.0.4 + +* Wed Feb 1 2012 Tomas Bzatek - 3.0.3-2 +- Enable bsdtar and bsdcpio in separate subpackages (#786400) + +* Fri Jan 13 2012 Tomas Bzatek - 3.0.3-1 +- Update to 3.0.3 + +* Fri Jan 13 2012 Fedora Release Engineering - 3.0.0-0.3.a +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Tue Nov 15 2011 Rex Dieter 3.0.0-0.2.a +- track files/sonames closer, so abi bumps aren't a surprise +- tighten subpkg deps via %%_isa + +* Mon Nov 14 2011 Tomas Bzatek - 3.0.0-0.1.a +- Update to 3.0.0a (alpha release) + +* Mon Sep 5 2011 Tomas Bzatek - 2.8.5-1 +- Update to 2.8.5 + +* Mon Feb 07 2011 Fedora Release Engineering - 2.8.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Thu Jan 13 2011 Tomas Bzatek - 2.8.4-2 +- Rebuild for new xz-libs + +* Wed Jun 30 2010 Tomas Bzatek - 2.8.4-1 +- Update to 2.8.4 + +* Fri Jun 25 2010 Tomas Bzatek - 2.8.3-2 +- Fix ISO9660 reader data type mismatches (#597243) + +* Tue Mar 16 2010 Tomas Bzatek - 2.8.3-1 +- Update to 2.8.3 + +* Mon Mar 8 2010 Tomas Bzatek - 2.8.1-1 +- Update to 2.8.1 + +* Fri Feb 5 2010 Tomas Bzatek - 2.8.0-1 +- Update to 2.8.0 + +* Wed Jan 6 2010 Tomas Bzatek - 2.7.902a-1 +- Update to 2.7.902a + +* Fri Aug 21 2009 Tomas Mraz - 2.7.1-2 +- rebuilt with new openssl + +* Fri Aug 7 2009 Tomas Bzatek 2.7.1-1 +- Update to 2.7.1 +- Drop deprecated lzma dependency, libxz handles both formats + +* Mon Jul 27 2009 Tomas Bzatek 2.7.0-3 +- Enable XZ compression format + +* Fri Jul 24 2009 Fedora Release Engineering - 2.7.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Tue May 12 2009 Tomas Bzatek 2.7.0-1 +- Update to 2.7.0 + +* Fri Mar 6 2009 Tomas Bzatek 2.6.2-1 +- Update to 2.6.2 + +* Wed Feb 25 2009 Fedora Release Engineering - 2.6.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Mon Feb 16 2009 Tomas Bzatek 2.6.1-1 +- Update to 2.6.1 + +* Thu Jan 8 2009 Tomas Bzatek 2.6.0-1 +- Update to 2.6.0 + +* Mon Dec 15 2008 Tomas Bzatek 2.5.904a-1 +- Update to 2.5.904a + +* Tue Dec 9 2008 Tomas Bzatek 2.5.903a-2 +- Add LZMA support + +* Mon Dec 8 2008 Tomas Bzatek 2.5.903a-1 +- Update to 2.5.903a + +* Tue Jul 22 2008 Tomas Bzatek 2.5.5-1 +- Update to 2.5.5 + +* Wed Apr 2 2008 Tomas Bzatek 2.4.17-1 +- Update to 2.4.17 + +* Wed Mar 19 2008 Tomas Bzatek 2.4.14-1 +- Initial packaging