From 9ec4999852604688ecaad8280120592d91e13c15 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Wed, 24 Jun 2026 00:57:36 +0800 Subject: [PATCH] Bump libarchive-clib to 3.8.8 --- libarchive-clib/build/autoconf/libtool.m4 | 2 +- libarchive-clib/build/autoconf/lt~obsolete.m4 | 99 +++ libarchive-clib/c/archive.h | 9 +- libarchive-clib/c/archive_acl.c | 442 +++++-------- libarchive-clib/c/archive_cmdline.c | 28 +- libarchive-clib/c/archive_crc32.h | 71 +- libarchive-clib/c/archive_cryptor.c | 4 +- libarchive-clib/c/archive_cryptor_private.h | 12 +- libarchive-clib/c/archive_digest.c | 69 +- libarchive-clib/c/archive_digest_private.h | 12 +- libarchive-clib/c/archive_endian.h | 41 ++ libarchive-clib/c/archive_entry.c | 4 +- libarchive-clib/c/archive_entry.h | 2 +- .../c/archive_entry_link_resolver.c | 13 +- libarchive-clib/c/archive_entry_sparse.c | 5 +- libarchive-clib/c/archive_entry_strmode.c | 45 +- libarchive-clib/c/archive_hmac.c | 2 +- libarchive-clib/c/archive_hmac_private.h | 2 +- libarchive-clib/c/archive_integer.h | 247 +++++++ libarchive-clib/c/archive_match.c | 45 +- libarchive-clib/c/archive_options.c | 18 +- libarchive-clib/c/archive_pack_dev.c | 2 +- libarchive-clib/c/archive_parse_date.c | 72 +- libarchive-clib/c/archive_pathmatch.c | 52 +- libarchive-clib/c/archive_random.c | 26 - libarchive-clib/c/archive_read.c | 86 ++- .../c/archive_read_append_filter.c | 28 +- libarchive-clib/c/archive_read_data_into_fd.c | 46 +- .../c/archive_read_disk_entry_from_file.c | 7 +- libarchive-clib/c/archive_read_disk_posix.c | 17 +- libarchive-clib/c/archive_read_disk_windows.c | 23 +- .../c/archive_read_open_filename.c | 7 +- .../c/archive_read_support_filter_all.c | 14 +- .../c/archive_read_support_filter_bzip2.c | 31 +- .../c/archive_read_support_filter_compress.c | 32 +- .../c/archive_read_support_filter_grzip.c | 5 +- .../c/archive_read_support_filter_gzip.c | 34 +- .../c/archive_read_support_filter_lrzip.c | 6 +- .../c/archive_read_support_filter_lz4.c | 127 ++-- .../c/archive_read_support_filter_lzop.c | 11 +- .../c/archive_read_support_filter_rpm.c | 216 +++--- .../c/archive_read_support_filter_uu.c | 27 +- .../c/archive_read_support_filter_xz.c | 18 +- .../c/archive_read_support_filter_zstd.c | 110 ++-- .../c/archive_read_support_format_7zip.c | 619 ++++++++++-------- .../c/archive_read_support_format_ar.c | 19 +- .../c/archive_read_support_format_cab.c | 85 ++- .../c/archive_read_support_format_cpio.c | 132 ++-- .../c/archive_read_support_format_iso9660.c | 75 ++- .../c/archive_read_support_format_lha.c | 112 +++- .../c/archive_read_support_format_mtree.c | 35 +- .../c/archive_read_support_format_rar.c | 115 ++-- .../c/archive_read_support_format_rar5.c | 219 ++++--- .../c/archive_read_support_format_tar.c | 281 +++++--- .../c/archive_read_support_format_xar.c | 236 ++++--- .../c/archive_read_support_format_zip.c | 377 ++++++++--- libarchive-clib/c/archive_string.c | 14 +- libarchive-clib/c/archive_time.c | 27 +- libarchive-clib/c/archive_util.c | 68 +- libarchive-clib/c/archive_version_details.c | 27 +- libarchive-clib/c/archive_windows.c | 29 +- libarchive-clib/c/archive_windows.h | 33 +- libarchive-clib/c/archive_write.c | 2 +- libarchive-clib/c/archive_write_add_filter.c | 2 +- .../c/archive_write_add_filter_b64encode.c | 6 +- .../c/archive_write_add_filter_bzip2.c | 4 - .../c/archive_write_add_filter_gzip.c | 28 +- .../c/archive_write_add_filter_lz4.c | 16 +- .../c/archive_write_add_filter_program.c | 4 + .../c/archive_write_add_filter_uuencode.c | 6 +- .../c/archive_write_add_filter_zstd.c | 2 - libarchive-clib/c/archive_write_disk_posix.c | 35 +- .../c/archive_write_disk_windows.c | 103 +-- .../c/archive_write_set_format_7zip.c | 9 +- .../c/archive_write_set_format_ar.c | 2 +- .../c/archive_write_set_format_cpio_binary.c | 14 +- .../c/archive_write_set_format_cpio_newc.c | 13 +- .../c/archive_write_set_format_cpio_odc.c | 13 +- .../c/archive_write_set_format_gnutar.c | 3 +- .../c/archive_write_set_format_iso9660.c | 115 +++- .../c/archive_write_set_format_mtree.c | 70 +- .../c/archive_write_set_format_pax.c | 14 +- .../c/archive_write_set_format_shar.c | 9 +- .../c/archive_write_set_format_ustar.c | 3 +- .../c/archive_write_set_format_v7tar.c | 3 +- .../c/archive_write_set_format_warc.c | 3 +- .../c/archive_write_set_format_xar.c | 82 ++- .../c/archive_write_set_format_zip.c | 16 +- libarchive-clib/c/config.h.in | 4 + libarchive-clib/c/filter_fork_windows.c | 67 +- libarchive-clib/c/xxhash.c | 2 + libarchive-clib/libarchive-clib.cabal | 4 +- 92 files changed, 3152 insertions(+), 2174 deletions(-) create mode 100644 libarchive-clib/build/autoconf/lt~obsolete.m4 create mode 100644 libarchive-clib/c/archive_integer.h diff --git a/libarchive-clib/build/autoconf/libtool.m4 b/libarchive-clib/build/autoconf/libtool.m4 index 8d323b3..4f51ccf 100644 --- a/libarchive-clib/build/autoconf/libtool.m4 +++ b/libarchive-clib/build/autoconf/libtool.m4 @@ -4825,7 +4825,7 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu* | *freebsd*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) diff --git a/libarchive-clib/build/autoconf/lt~obsolete.m4 b/libarchive-clib/build/autoconf/lt~obsolete.m4 new file mode 100644 index 0000000..22b5346 --- /dev/null +++ b/libarchive-clib/build/autoconf/lt~obsolete.m4 @@ -0,0 +1,99 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007, 2009, 2011-2019, 2021-2024 Free +# Software Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/libarchive-clib/c/archive.h b/libarchive-clib/c/archive.h index 41a5440..159a3d5 100644 --- a/libarchive-clib/c/archive.h +++ b/libarchive-clib/c/archive.h @@ -34,7 +34,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3008007 +#define ARCHIVE_VERSION_NUMBER 3008008 #include #include /* for wchar_t */ @@ -177,7 +177,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.8.7" +#define ARCHIVE_VERSION_ONLY_STRING "3.8.8" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); @@ -210,7 +210,9 @@ __LA_DECL const char * archive_openssl_version(void); __LA_DECL const char * archive_libmd_version(void); __LA_DECL const char * archive_commoncrypto_version(void); __LA_DECL const char * archive_cng_version(void); +#if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL const char * archive_wincrypt_version(void); +#endif __LA_DECL const char * archive_librichacl_version(void); __LA_DECL const char * archive_libacl_version(void); __LA_DECL const char * archive_libattr_version(void); @@ -653,7 +655,6 @@ __LA_DECL int archive_read_data_block(struct archive *a, /*- * Some convenience functions that are built on archive_read_data: * 'skip': skips entire entry - * 'into_buffer': writes data into memory buffer that you provide * 'into_fd': writes data to specified filedes */ __LA_DECL int archive_read_data_skip(struct archive *); @@ -1234,7 +1235,7 @@ __LA_DECL int archive_match_exclude_entry(struct archive *, int _flag, struct archive_entry *); /* - * Test if a file is excluded by its uid ,gid, uname or gname. + * Test if a file is excluded by its uid, gid, uname or gname. * The conditions are set by following functions. */ __LA_DECL int archive_match_owner_excluded(struct archive *, diff --git a/libarchive-clib/c/archive_acl.c b/libarchive-clib/c/archive_acl.c index ab60183..f8d85b4 100644 --- a/libarchive-clib/c/archive_acl.c +++ b/libarchive-clib/c/archive_acl.c @@ -56,9 +56,7 @@ static int archive_acl_add_entry_len_l(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name, size_t len, struct archive_string_conv *sc); static int archive_acl_text_want_type(struct archive_acl *acl, int flags); -static size_t archive_acl_text_len(struct archive_acl *acl, int want_type, - int flags, int wide, struct archive *a, - struct archive_string_conv *sc); +static size_t archive_acl_text_empty(struct archive_acl *acl, int want_type); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, @@ -67,9 +65,9 @@ static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); -static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, +static void append_entry_w(struct archive_wstring *ws, const wchar_t *prefix, int type, int tag, int flags, const wchar_t *wname, int perm, int id); -static void append_id_w(wchar_t **wp, int id); +static void append_id_w(struct archive_wstring *ws, int id); static int isint(const char *start, const char *end, int *result); static int ismode(const char *start, const char *end, int *result); static int is_nfs4_flags(const char *start, const char *end, @@ -78,9 +76,9 @@ static int is_nfs4_perms(const char *start, const char *end, int *result); static void next_field(const char **p, size_t *l, const char **start, const char **end, char *sep); -static void append_entry(char **p, const char *prefix, int type, +static void append_entry(struct archive_string *s, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id); -static void append_id(char **p, int id); +static void append_id(struct archive_string *s, int id); static const struct { const int perm; @@ -539,20 +537,12 @@ archive_acl_text_want_type(struct archive_acl *acl, int flags) } /* - * Calculate ACL text string length + * Check if ACL text would be empty */ static size_t -archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, - int wide, struct archive *a, struct archive_string_conv *sc) { +archive_acl_text_empty(struct archive_acl *acl, int want_type) { struct archive_acl_entry *ap; - const char *name; - const wchar_t *wname; - int count, idlen, tmp, r; - size_t length; - size_t len; - count = 0; - length = 0; for (ap = acl->acl_head; ap != NULL; ap = ap->next) { if ((ap->type & want_type) == 0) continue; @@ -565,107 +555,11 @@ archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) continue; - count++; - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 - && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) - length += 8; /* "default:" */ - switch (ap->tag) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - length += 6; /* "owner@" */ - break; - } - /* FALLTHROUGH */ - case ARCHIVE_ENTRY_ACL_USER: - case ARCHIVE_ENTRY_ACL_MASK: - length += 4; /* "user", "mask" */ - break; - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - length += 6; /* "group@" */ - break; - } - /* FALLTHROUGH */ - case ARCHIVE_ENTRY_ACL_GROUP: - case ARCHIVE_ENTRY_ACL_OTHER: - length += 5; /* "group", "other" */ - break; - case ARCHIVE_ENTRY_ACL_EVERYONE: - length += 9; /* "everyone@" */ - break; - } - length += 1; /* colon after tag */ - if (ap->tag == ARCHIVE_ENTRY_ACL_USER || - ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { - if (wide) { - r = archive_mstring_get_wcs(a, &ap->name, - &wname); - if (r == 0 && wname != NULL) - length += wcslen(wname); - else if (r < 0 && errno == ENOMEM) - return (0); - else - length += sizeof(uid_t) * 3 + 1; - } else { - r = archive_mstring_get_mbs_l(a, &ap->name, &name, - &len, sc); - if (r != 0) - return (0); - if (len > 0 && name != NULL) - length += len; - else - length += sizeof(uid_t) * 3 + 1; - } - length += 1; /* colon after user or group name */ - } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) - length += 1; /* 2nd colon empty user,group or other */ - - if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) - && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) - && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER - || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) { - /* Solaris has no colon after other: and mask: */ - length = length - 1; - } - if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - /* rwxpdDaARWcCos:fdinSFI:deny */ - length += 27; - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0) - length += 1; /* allow, alarm, audit */ - } else - length += 3; /* rwx */ - - if ((ap->tag == ARCHIVE_ENTRY_ACL_USER || - ap->tag == ARCHIVE_ENTRY_ACL_GROUP) && - (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) { - length += 1; /* colon */ - /* ID digit count */ - idlen = 1; - tmp = ap->id; - while (tmp > 9) { - tmp = tmp / 10; - idlen++; - } - length += idlen; - } - length ++; /* entry separator */ - } - - /* Add filemode-mapping access entries to the length */ - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) { - /* "user::rwx\ngroup::rwx\nother:rwx\n" */ - length += 31; - } else { - /* "user::rwx\ngroup::rwx\nother::rwx\n" */ - length += 32; - } - } else if (count == 0) return (0); + } - /* The terminating character is included in count */ - return (length); + return (1); } /* @@ -676,15 +570,12 @@ wchar_t * archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, struct archive *a) { - int count; - size_t length; - size_t len; const wchar_t *wname; const wchar_t *prefix; wchar_t separator; struct archive_acl_entry *ap; + struct archive_wstring ws; int id, r, want_type; - wchar_t *wp, *ws; want_type = archive_acl_text_want_type(acl, flags); @@ -695,9 +586,7 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; - length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL); - - if (length == 0) + if (archive_acl_text_empty(acl, want_type)) return (NULL); if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) @@ -705,28 +594,20 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, else separator = L'\n'; - /* Now, allocate the string and actually populate it. */ - wp = ws = malloc(length * sizeof(*wp)); - if (wp == NULL) { - if (errno == ENOMEM) - __archive_errx(1, "No memory"); - return (NULL); - } - count = 0; + archive_string_init(&ws); if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); - *wp++ = separator; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_wstrappend_wchar(&ws, separator); + append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); - *wp++ = separator; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_wstrappend_wchar(&ws, separator); + append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); - count += 3; } for (ap = acl->acl_head; ap != NULL; ap = ap->next) { @@ -748,108 +629,95 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, prefix = NULL; r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0) { - if (count > 0) - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + if (ws.length > 0) + archive_wstrappend_wchar(&ws, separator); + if ((flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) || + wname == NULL) id = ap->id; else id = -1; - append_entry_w(&wp, prefix, ap->type, ap->tag, flags, + append_entry_w(&ws, prefix, ap->type, ap->tag, flags, wname, ap->permset, id); - count++; } else if (r < 0 && errno == ENOMEM) { - free(ws); + archive_wstring_free(&ws); return (NULL); } } - /* Add terminating character */ - *wp++ = L'\0'; - - len = wcslen(ws); - - if (len > length - 1) - __archive_errx(1, "Buffer overrun"); - if (text_len != NULL) - *text_len = len; + *text_len = ws.length; - return (ws); + return (ws.s); } static void -append_id_w(wchar_t **wp, int id) +append_id_w(struct archive_wstring *ws, int id) { if (id < 0) id = 0; if (id > 9) - append_id_w(wp, id / 10); - *(*wp)++ = L"0123456789"[id % 10]; + append_id_w(ws, id / 10); + archive_wstrappend_wchar(ws, L"0123456789"[id % 10]); } static void -append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, +append_entry_w(struct archive_wstring *ws, const wchar_t *prefix, int type, int tag, int flags, const wchar_t *wname, int perm, int id) { int i; - if (prefix != NULL) { - wcscpy(*wp, prefix); - *wp += wcslen(*wp); - } + if (prefix != NULL) + archive_wstrcat(ws, prefix); switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - wcscpy(*wp, L"owner@"); + archive_wstrcat(ws, L"owner@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: - wcscpy(*wp, L"user"); + archive_wstrcat(ws, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - wcscpy(*wp, L"group@"); + archive_wstrcat(ws, L"group@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: - wcscpy(*wp, L"group"); + archive_wstrcat(ws, L"group"); break; case ARCHIVE_ENTRY_ACL_MASK: - wcscpy(*wp, L"mask"); + archive_wstrcat(ws, L"mask"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: - wcscpy(*wp, L"other"); + archive_wstrcat(ws, L"other"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_EVERYONE: - wcscpy(*wp, L"everyone@"); + archive_wstrcat(ws, L"everyone@"); wname = NULL; id = -1; break; default: - **wp = '\0'; break; } - *wp += wcslen(*wp); - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { if (wname != NULL) { - wcscpy(*wp, wname); - *wp += wcslen(*wp); + archive_wstrcat(ws, wname); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id_w(wp, id); + append_id_w(ws, id); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) id = -1; } @@ -857,51 +725,49 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) || (tag != ARCHIVE_ENTRY_ACL_OTHER && tag != ARCHIVE_ENTRY_ACL_MASK)) - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); } if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { /* POSIX.1e ACL perms */ - *(*wp)++ = (perm & 0444) ? L'r' : L'-'; - *(*wp)++ = (perm & 0222) ? L'w' : L'-'; - *(*wp)++ = (perm & 0111) ? L'x' : L'-'; + archive_wstrappend_wchar(ws, (perm & 0444) ? L'r' : L'-'); + archive_wstrappend_wchar(ws, (perm & 0222) ? L'w' : L'-'); + archive_wstrappend_wchar(ws, (perm & 0111) ? L'x' : L'-'); } else { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) - *(*wp)++ = nfsv4_acl_perm_map[i].wc; + archive_wstrappend_wchar(ws, nfsv4_acl_perm_map[i].wc); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*wp)++ = L'-'; + archive_wstrappend_wchar(ws, L'-'); } - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) - *(*wp)++ = nfsv4_acl_flag_map[i].wc; + archive_wstrappend_wchar(ws, nfsv4_acl_flag_map[i].wc); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*wp)++ = L'-'; + archive_wstrappend_wchar(ws, L'-'); } - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - wcscpy(*wp, L"allow"); + archive_wstrcat(ws, L"allow"); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: - wcscpy(*wp, L"deny"); + archive_wstrcat(ws, L"deny"); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - wcscpy(*wp, L"audit"); + archive_wstrcat(ws, L"audit"); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - wcscpy(*wp, L"alarm"); + archive_wstrcat(ws, L"alarm"); break; default: - *(*wp) = L'\0'; break; } - *wp += wcslen(*wp); } if (id != -1) { - *(*wp)++ = L':'; - append_id_w(wp, id); + archive_wstrappend_wchar(ws, L':'); + append_id_w(ws, id); } } @@ -913,15 +779,13 @@ char * archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, struct archive_string_conv *sc) { - int count; - size_t length; size_t len; const char *name; const char *prefix; char separator; struct archive_acl_entry *ap; + struct archive_string s; int id, r, want_type; - char *p, *s; want_type = archive_acl_text_want_type(acl, flags); @@ -932,9 +796,7 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; - length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc); - - if (length == 0) + if (archive_acl_text_empty(acl, want_type)) return (NULL); if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) @@ -942,28 +804,20 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, else separator = '\n'; - /* Now, allocate the string and actually populate it. */ - p = s = malloc(length * sizeof(*p)); - if (p == NULL) { - if (errno == ENOMEM) - __archive_errx(1, "No memory"); - return (NULL); - } - count = 0; + archive_string_init(&s); if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); - *p++ = separator; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_strappend_char(&s, separator); + append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); - *p++ = separator; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_strappend_char(&s, separator); + append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); - count += 3; } for (ap = acl->acl_head; ap != NULL; ap = ap->next) { @@ -986,109 +840,95 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, r = archive_mstring_get_mbs_l( NULL, &ap->name, &name, &len, sc); if (r != 0) { - free(s); + archive_string_free(&s); return (NULL); } - if (count > 0) - *p++ = separator; + if (s.length > 0) + archive_strappend_char(&s, separator); if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { id = ap->id; } else { id = -1; } - append_entry(&p, prefix, ap->type, ap->tag, flags, name, + append_entry(&s, prefix, ap->type, ap->tag, flags, name, ap->permset, id); - count++; } - /* Add terminating character */ - *p++ = '\0'; - - len = strlen(s); - - if (len > length - 1) - __archive_errx(1, "Buffer overrun"); - if (text_len != NULL) - *text_len = len; + *text_len = s.length; - return (s); + return (s.s); } static void -append_id(char **p, int id) +append_id(struct archive_string *s, int id) { if (id < 0) id = 0; if (id > 9) - append_id(p, id / 10); - *(*p)++ = "0123456789"[id % 10]; + append_id(s, id / 10); + archive_strappend_char(s, "0123456789"[id % 10]); } static void -append_entry(char **p, const char *prefix, int type, +append_entry(struct archive_string *s, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id) { int i; - if (prefix != NULL) { - strcpy(*p, prefix); - *p += strlen(*p); - } + if (prefix != NULL) + archive_strcat(s, prefix); switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: name = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - strcpy(*p, "owner@"); + archive_strcat(s, "owner@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: - strcpy(*p, "user"); + archive_strcat(s, "user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: name = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - strcpy(*p, "group@"); + archive_strcat(s, "group@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: - strcpy(*p, "group"); + archive_strcat(s, "group"); break; case ARCHIVE_ENTRY_ACL_MASK: - strcpy(*p, "mask"); + archive_strcat(s, "mask"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: - strcpy(*p, "other"); + archive_strcat(s, "other"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_EVERYONE: - strcpy(*p, "everyone@"); + archive_strcat(s, "everyone@"); name = NULL; id = -1; break; default: - **p = '\0'; break; } - *p += strlen(*p); - *(*p)++ = ':'; + archive_strappend_char(s, ':'); if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { if (name != NULL) { - strcpy(*p, name); - *p += strlen(*p); + archive_strcat(s, name); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id(p, id); + append_id(s, id); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) id = -1; } @@ -1096,51 +936,49 @@ append_entry(char **p, const char *prefix, int type, if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) || (tag != ARCHIVE_ENTRY_ACL_OTHER && tag != ARCHIVE_ENTRY_ACL_MASK)) - *(*p)++ = ':'; + archive_strappend_char(s, ':'); } if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { /* POSIX.1e ACL perms */ - *(*p)++ = (perm & 0444) ? 'r' : '-'; - *(*p)++ = (perm & 0222) ? 'w' : '-'; - *(*p)++ = (perm & 0111) ? 'x' : '-'; + archive_strappend_char(s, (perm & 0444) ? 'r' : '-'); + archive_strappend_char(s, (perm & 0222) ? 'w' : '-'); + archive_strappend_char(s, (perm & 0111) ? 'x' : '-'); } else { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) - *(*p)++ = nfsv4_acl_perm_map[i].c; + archive_strappend_char(s, nfsv4_acl_perm_map[i].c); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*p)++ = '-'; + archive_strappend_char(s, '-'); } - *(*p)++ = ':'; + archive_strappend_char(s, ':'); for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) - *(*p)++ = nfsv4_acl_flag_map[i].c; + archive_strappend_char(s, nfsv4_acl_flag_map[i].c); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*p)++ = '-'; + archive_strappend_char(s, '-'); } - *(*p)++ = ':'; + archive_strappend_char(s, ':'); switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - strcpy(*p, "allow"); + archive_strcat(s, "allow"); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: - strcpy(*p, "deny"); + archive_strcat(s, "deny"); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - strcpy(*p, "audit"); + archive_strcat(s, "audit"); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - strcpy(*p, "alarm"); + archive_strcat(s, "alarm"); break; default: - *(*p) = '\0'; break; } - *p += strlen(*p); } if (id != -1) { - *(*p)++ = ':'; - append_id(p, id); + archive_strappend_char(s, ':'); + append_id(s, id); } } @@ -1248,11 +1086,18 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, type = want_type; /* Check for a numeric ID in field n+1 or n+3. */ - isint_w(field[n + 1].start, field[n + 1].end, &id); + if (isint_w(field[n + 1].start, field[n + 1].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } /* Field n+3 is optional. */ - if (id == -1 && fields > n+3) - isint_w(field[n + 3].start, field[n + 3].end, - &id); + if (id == -1 && fields > n+3 && + isint_w(field[n + 3].start, field[n + 3].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } tag = 0; s = field[n].start; @@ -1367,7 +1212,10 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, tag == ARCHIVE_ENTRY_ACL_GROUP) { n = 1; name = field[1]; - isint_w(name.start, name.end, &id); + if (isint_w(name.start, name.end, &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } else n = 0; @@ -1402,7 +1250,11 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, ret = ARCHIVE_WARN; continue; } - isint_w(field[4 + n].start, field[4 + n].end, &id); + if (isint_w(field[4 + n].start, field[4 + n].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } /* Add entry to the internal list. */ @@ -1436,8 +1288,8 @@ isint_w(const wchar_t *start, const wchar_t *end, int *result) if (*start < L'0' || *start > L'9') return (0); if (n > (INT_MAX / 10) || - (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) { - n = INT_MAX; + (n == INT_MAX / 10 && (*start - L'0') >= INT_MAX % 10)) { + return (-1); } else { n *= 10; n += *start - L'0'; @@ -1747,15 +1599,21 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, type = want_type; /* Check for a numeric ID in field n+1 or n+3. */ - isint(field[n + 1].start, field[n + 1].end, &id); + if (isint(field[n + 1].start, field[n + 1].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } /* Field n+3 is optional. */ - if (id == -1 && fields > (n + 3)) - isint(field[n + 3].start, field[n + 3].end, - &id); + if (id == -1 && fields > (n + 3) && + isint(field[n + 3].start, field[n + 3].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } tag = 0; s = field[n].start; - st = field[n].start + 1; len = field[n].end - field[n].start; if (len == 0) { @@ -1763,6 +1621,8 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, continue; } + st = s + 1; + switch (*s) { case 'u': if (len == 1 || (len == 4 @@ -1868,7 +1728,10 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, tag == ARCHIVE_ENTRY_ACL_GROUP) { n = 1; name = field[1]; - isint(name.start, name.end, &id); + if (isint(name.start, name.end, &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } else n = 0; @@ -1903,8 +1766,11 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, ret = ARCHIVE_WARN; continue; } - isint(field[4 + n].start, field[4 + n].end, - &id); + if (isint(field[4 + n].start, field[4 + n].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } /* Add entry to the internal list. */ @@ -1938,8 +1804,8 @@ isint(const char *start, const char *end, int *result) if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10) || - (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { - n = INT_MAX; + (n == INT_MAX / 10 && (*start - '0') >= INT_MAX % 10)) { + return (-1); } else { n *= 10; n += *start - '0'; @@ -2121,7 +1987,10 @@ next_field(const char **p, size_t *l, const char **start, (*p)++; (*l)--; } - *sep = **p; + if (*l > 0) + *sep = **p; + else + *sep = '\0'; /* Handle in-field comments */ if (*sep == '#') { @@ -2129,7 +1998,10 @@ next_field(const char **p, size_t *l, const char **start, (*p)++; (*l)--; } - *sep = **p; + if (*l > 0) + *sep = **p; + else + *sep = '\0'; } /* Skip separator. */ diff --git a/libarchive-clib/c/archive_cmdline.c b/libarchive-clib/c/archive_cmdline.c index 23bb05d..ffc4f54 100644 --- a/libarchive-clib/c/archive_cmdline.c +++ b/libarchive-clib/c/archive_cmdline.c @@ -36,7 +36,6 @@ #include "archive_cmdline_private.h" #include "archive_string.h" -static int cmdline_set_path(struct archive_cmdline *, const char *); static int cmdline_add_arg(struct archive_cmdline *, const char *); static ssize_t @@ -98,7 +97,7 @@ get_argument(struct archive_string *as, const char *p) /* * Set up command line arguments. - * Returns ARCHIVE_OK if everything okey. + * Returns ARCHIVE_OK if everything okay. * Returns ARCHIVE_FAILED if there is a lack of the `"' terminator or an * empty command line. * Returns ARCHIVE_FATAL if no memory. @@ -123,9 +122,12 @@ __archive_cmdline_parse(struct archive_cmdline *data, const char *cmd) r = ARCHIVE_FAILED;/* An empty command path. */ goto exit_function; } - r = cmdline_set_path(data, as.s); - if (r != ARCHIVE_OK) + free(data->path); + data->path = strdup(as.s); + if (data->path == NULL) { + r = ARCHIVE_FATAL; goto exit_function; + } p = strrchr(as.s, '/'); if (p == NULL) p = as.s; @@ -158,23 +160,7 @@ __archive_cmdline_parse(struct archive_cmdline *data, const char *cmd) } /* - * Set the program path. - */ -static int -cmdline_set_path(struct archive_cmdline *data, const char *path) -{ - char *newptr; - - newptr = realloc(data->path, strlen(path) + 1); - if (newptr == NULL) - return (ARCHIVE_FATAL); - data->path = newptr; - strcpy(data->path, path); - return (ARCHIVE_OK); -} - -/* - * Add a argument for the program. + * Add an argument for the program. */ static int cmdline_add_arg(struct archive_cmdline *data, const char *arg) diff --git a/libarchive-clib/c/archive_crc32.h b/libarchive-clib/c/archive_crc32.h index d86a507..cbd2529 100644 --- a/libarchive-clib/c/archive_crc32.h +++ b/libarchive-clib/c/archive_crc32.h @@ -43,28 +43,65 @@ static unsigned long crc32(unsigned long crc, const void *_p, size_t len) { - unsigned long crc2, b, i; const unsigned char *p = _p; - static volatile int crc_tbl_inited = 0; - static unsigned long crc_tbl[256]; + static const unsigned long crc_tbl[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d + }; if (_p == NULL) return (0); - if (!crc_tbl_inited) { - for (b = 0; b < 256; ++b) { - crc2 = b; - for (i = 8; i > 0; --i) { - if (crc2 & 1) - crc2 = (crc2 >> 1) ^ 0xedb88320UL; - else - crc2 = (crc2 >> 1); - } - crc_tbl[b] = crc2; - } - crc_tbl_inited = 1; - } - crc = crc ^ 0xffffffffUL; /* A use of this loop is about 20% - 30% faster than * no use version in any optimization option of gcc. */ diff --git a/libarchive-clib/c/archive_cryptor.c b/libarchive-clib/c/archive_cryptor.c index b6a02fd..85b20ac 100644 --- a/libarchive-clib/c/archive_cryptor.c +++ b/libarchive-clib/c/archive_cryptor.c @@ -57,7 +57,7 @@ pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, return 0; } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #ifdef _MSC_VER #pragma comment(lib, "Bcrypt.lib") #endif @@ -197,7 +197,7 @@ aes_ctr_release(archive_crypto_ctx *ctx) return 0; } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) diff --git a/libarchive-clib/c/archive_cryptor_private.h b/libarchive-clib/c/archive_cryptor_private.h index 1f9298f..069eccd 100644 --- a/libarchive-clib/c/archive_cryptor_private.h +++ b/libarchive-clib/c/archive_cryptor_private.h @@ -62,7 +62,7 @@ typedef struct { size_t encr_pos; } archive_crypto_ctx; -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #include #define ARCHIVE_CRYPTOR_USE_CNG 1 @@ -148,16 +148,6 @@ typedef struct { #else -#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA512_WIN) -#if defined(_WIN32) && !defined(__CYGWIN__) && !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA) -#define ARCHIVE_CRYPTOR_USE_WINCRYPT 1 -#endif -#endif - #define AES_BLOCK_SIZE 16 #define AES_MAX_KEY_SIZE 32 typedef int archive_crypto_ctx; diff --git a/libarchive-clib/c/archive_digest.c b/libarchive-clib/c/archive_digest.c index 45fc36a..ab6db12 100644 --- a/libarchive-clib/c/archive_digest.c +++ b/libarchive-clib/c/archive_digest.c @@ -44,16 +44,11 @@ /* * Message digest functions for Windows platform. */ -#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA512_WIN) +#if defined(HAVE_BCRYPT_H) /* * Initialize a Message digest. */ -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA static int win_crypto_init(Digest_CTX *ctx, const WCHAR *algo) { @@ -72,30 +67,6 @@ win_crypto_init(Digest_CTX *ctx, const WCHAR *algo) ctx->valid = 1; return (ARCHIVE_OK); } -#else -static int -win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId) -{ - - ctx->valid = 0; - if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, - prov, CRYPT_VERIFYCONTEXT)) { - if (GetLastError() != (DWORD)NTE_BAD_KEYSET) - return (ARCHIVE_FAILED); - if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, - prov, CRYPT_NEWKEYSET)) - return (ARCHIVE_FAILED); - } - - if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) { - CryptReleaseContext(ctx->cryptProv, 0); - return (ARCHIVE_FAILED); - } - - ctx->valid = 1; - return (ARCHIVE_OK); -} -#endif /* * Update a Message digest. @@ -107,42 +78,26 @@ win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len) if (!ctx->valid) return (ARCHIVE_FAILED); -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)buf, (ULONG)len, 0); -#else - CryptHashData(ctx->hash, - (unsigned char *)(uintptr_t)buf, - (DWORD)len, 0); -#endif return (ARCHIVE_OK); } static int win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx) { -#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA) - DWORD siglen = (DWORD)bufsize; -#endif - if (!ctx->valid) return (ARCHIVE_FAILED); -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0); BCryptDestroyHash(ctx->hHash); BCryptCloseAlgorithmProvider(ctx->hAlg, 0); -#else - CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0); - CryptDestroyHash(ctx->hash); - CryptReleaseContext(ctx->cryptProv, 0); -#endif ctx->valid = 0; return (ARCHIVE_OK); } -#endif /* defined(ARCHIVE_CRYPTO_*_WIN) */ +#endif /* defined(HAVE_BCRYPT_H) */ /* MD5 implementations */ @@ -234,11 +189,7 @@ __archive_md5final(archive_md5_ctx *ctx, void *md) static int __archive_md5init(archive_md5_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5)); -#endif } static int @@ -645,11 +596,7 @@ __archive_sha1final(archive_sha1_ctx *ctx, void *md) static int __archive_sha1init(archive_sha1_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1)); -#endif } static int @@ -925,11 +872,7 @@ __archive_sha256final(archive_sha256_ctx *ctx, void *md) static int __archive_sha256init(archive_sha256_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256)); -#endif } static int @@ -1177,11 +1120,7 @@ __archive_sha384final(archive_sha384_ctx *ctx, void *md) static int __archive_sha384init(archive_sha384_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384)); -#endif } static int @@ -1453,11 +1392,7 @@ __archive_sha512final(archive_sha512_ctx *ctx, void *md) static int __archive_sha512init(archive_sha512_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512)); -#endif } static int diff --git a/libarchive-clib/c/archive_digest_private.h b/libarchive-clib/c/archive_digest_private.h index deb134e..7db9994 100644 --- a/libarchive-clib/c/archive_digest_private.h +++ b/libarchive-clib/c/archive_digest_private.h @@ -165,8 +165,7 @@ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA -/* don't use bcrypt when XP needs to be supported */ +#if defined(HAVE_BCRYPT_H) #include #define ARCHIVE_CRYPTO_CNG 1 typedef struct { @@ -174,15 +173,6 @@ typedef struct { BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; } Digest_CTX; -#else -#include -#include -#define ARCHIVE_CRYPTO_WINCRYPT 1 -typedef struct { - int valid; - HCRYPTPROV cryptProv; - HCRYPTHASH hash; -} Digest_CTX; #endif #endif diff --git a/libarchive-clib/c/archive_endian.h b/libarchive-clib/c/archive_endian.h index 83b2efa..939acc6 100644 --- a/libarchive-clib/c/archive_endian.h +++ b/libarchive-clib/c/archive_endian.h @@ -70,6 +70,22 @@ archive_be16dec(const void *pp) return ((p0 << 8) | p1); } +static inline uint32_t +archive_be24dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + /* Store into unsigned temporaries before left shifting, to avoid + promotion to signed int and then left shifting into the sign bit, + which is undefined behaviour. */ + unsigned int p2 = p[2]; + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p0 << 16) | (p1 << 8) | p2); +} + + static inline uint32_t archive_be32dec(const void *pp) { @@ -108,6 +124,21 @@ archive_le16dec(const void *pp) return ((p1 << 8) | p0); } +static inline uint32_t +archive_le24dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + /* Store into unsigned temporaries before left shifting, to avoid + promotion to signed int and then left shifting into the sign bit, + which is undefined behaviour. */ + unsigned int p2 = p[2]; + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p2 << 16) | (p1 << 8) | p0); +} + static inline uint32_t archive_le32dec(const void *pp) { @@ -170,6 +201,16 @@ archive_le16enc(void *pp, uint16_t u) p[1] = (u >> 8) & 0xff; } +static inline void +archive_le24enc(void *pp, uint32_t u) +{ + unsigned char *p = (unsigned char *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; +} + static inline void archive_le32enc(void *pp, uint32_t u) { diff --git a/libarchive-clib/c/archive_entry.c b/libarchive-clib/c/archive_entry.c index dc74430..90cf5cc 100644 --- a/libarchive-clib/c/archive_entry.c +++ b/libarchive-clib/c/archive_entry.c @@ -1239,7 +1239,7 @@ _archive_entry_copy_link_l(struct archive_entry *entry, } void -archive_entry_set_mode(struct archive_entry *entry, mode_t m) +archive_entry_set_mode(struct archive_entry *entry, __LA_MODE_T m) { entry->stat_valid = 0; entry->acl.mode = m; @@ -1314,7 +1314,7 @@ _archive_entry_copy_pathname_l(struct archive_entry *entry, } void -archive_entry_set_perm(struct archive_entry *entry, mode_t p) +archive_entry_set_perm(struct archive_entry *entry, __LA_MODE_T p) { entry->stat_valid = 0; entry->acl.mode &= AE_IFMT; diff --git a/libarchive-clib/c/archive_entry.h b/libarchive-clib/c/archive_entry.h index 7122a74..aa4f4df 100644 --- a/libarchive-clib/c/archive_entry.h +++ b/libarchive-clib/c/archive_entry.h @@ -28,7 +28,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3008007 +#define ARCHIVE_VERSION_NUMBER 3008008 /* * Note: archive_entry.h is for use outside of libarchive; the diff --git a/libarchive-clib/c/archive_entry_link_resolver.c b/libarchive-clib/c/archive_entry_link_resolver.c index 77fcad6..233c46a 100644 --- a/libarchive-clib/c/archive_entry_link_resolver.c +++ b/libarchive-clib/c/archive_entry_link_resolver.c @@ -78,7 +78,7 @@ struct links_entry { struct archive_entry_linkresolver { struct links_entry **buckets; struct links_entry *spare; - unsigned long number_entries; + size_t number_entries; size_t number_buckets; int strategy; }; @@ -158,13 +158,12 @@ archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res, void archive_entry_linkresolver_free(struct archive_entry_linkresolver *res) { - struct links_entry *le; - if (res == NULL) return; - while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL) - archive_entry_free(le->entry); + while (next_entry(res, NEXT_ENTRY_ALL) != NULL) { + /* Actual freeing done by next_entry() */ + } free(res->buckets); free(res); } @@ -382,6 +381,10 @@ insert_entry(struct archive_entry_linkresolver *res, if (le == NULL) return (NULL); le->canonical = archive_entry_clone(entry); + if (le->canonical == NULL) { + free(le); + return (NULL); + } /* If the links cache is getting too full, enlarge the hash table. */ if (res->number_entries > res->number_buckets * 2) diff --git a/libarchive-clib/c/archive_entry_sparse.c b/libarchive-clib/c/archive_entry_sparse.c index c430896..1e84ff7 100644 --- a/libarchive-clib/c/archive_entry_sparse.c +++ b/libarchive-clib/c/archive_entry_sparse.c @@ -127,9 +127,10 @@ archive_entry_sparse_count(struct archive_entry *entry) int archive_entry_sparse_reset(struct archive_entry * entry) { + /* Counting can change sparse_head, so do it first */ + int count = archive_entry_sparse_count(entry); entry->sparse_p = entry->sparse_head; - - return archive_entry_sparse_count(entry); + return (count); } int diff --git a/libarchive-clib/c/archive_entry_strmode.c b/libarchive-clib/c/archive_entry_strmode.c index 5faa2fa..2217199 100644 --- a/libarchive-clib/c/archive_entry_strmode.c +++ b/libarchive-clib/c/archive_entry_strmode.c @@ -28,9 +28,6 @@ #ifdef HAVE_SYS_STAT_H #include #endif -#ifdef HAVE_STRING_H -#include -#endif #include "archive_entry.h" #include "archive_entry_private.h" @@ -38,16 +35,10 @@ const char * archive_entry_strmode(struct archive_entry *entry) { - static const mode_t permbits[] = - { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 }; char *bp = entry->strmode; - mode_t mode; + mode_t mask, mode; int i; - /* Fill in a default string, then selectively override. */ - strcpy(bp, "?rwxrwxrwx "); - - mode = archive_entry_mode(entry); switch (archive_entry_filetype(entry)) { case AE_IFREG: bp[0] = '-'; break; case AE_IFBLK: bp[0] = 'b'; break; @@ -57,30 +48,22 @@ archive_entry_strmode(struct archive_entry *entry) case AE_IFSOCK: bp[0] = 's'; break; case AE_IFIFO: bp[0] = 'p'; break; default: - if (archive_entry_hardlink(entry) != NULL) { - bp[0] = 'h'; - break; - } + bp[0] = (archive_entry_hardlink(entry) != NULL) ? 'h' : '?'; + break; } - for (i = 0; i < 9; i++) - if (!(mode & permbits[i])) - bp[i+1] = '-'; + mode = archive_entry_mode(entry); + for (i = 0, mask = 0400; i < 9; i++, mask >>= 1) + bp[i + 1] = (mode & mask) ? "rwx"[i % 3] : '-'; - if (mode & S_ISUID) { - if (mode & 0100) bp[3] = 's'; - else bp[3] = 'S'; - } - if (mode & S_ISGID) { - if (mode & 0010) bp[6] = 's'; - else bp[6] = 'S'; - } - if (mode & S_ISVTX) { - if (mode & 0001) bp[9] = 't'; - else bp[9] = 'T'; - } - if (archive_entry_acl_types(entry) != 0) - bp[10] = '+'; + if (mode & S_ISUID) + bp[3] = (mode & 0100) ? 's' : 'S'; + if (mode & S_ISGID) + bp[6] = (mode & 0010) ? 's' : 'S'; + if (mode & S_ISVTX) + bp[9] = (mode & 0001) ? 't' : 'T'; + bp[10] = (archive_entry_acl_types(entry) != 0) ? '+' : ' '; + bp[11] = '\0'; return (bp); } diff --git a/libarchive-clib/c/archive_hmac.c b/libarchive-clib/c/archive_hmac.c index 458092f..e1e8f05 100644 --- a/libarchive-clib/c/archive_hmac.c +++ b/libarchive-clib/c/archive_hmac.c @@ -74,7 +74,7 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) memset(ctx, 0, sizeof(*ctx)); } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #ifndef BCRYPT_HASH_REUSABLE_FLAG # define BCRYPT_HASH_REUSABLE_FLAG 0x00000020 diff --git a/libarchive-clib/c/archive_hmac_private.h b/libarchive-clib/c/archive_hmac_private.h index 36b8e33..af00bea 100644 --- a/libarchive-clib/c/archive_hmac_private.h +++ b/libarchive-clib/c/archive_hmac_private.h @@ -52,7 +52,7 @@ int __libarchive_hmac_build_hack(void); typedef CCHmacContext archive_hmac_sha1_ctx; -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #include typedef struct { diff --git a/libarchive-clib/c/archive_integer.h b/libarchive-clib/c/archive_integer.h new file mode 100644 index 0000000..6427c62 --- /dev/null +++ b/libarchive-clib/c/archive_integer.h @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2026 Tobias Stoeckmann + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +#ifndef ARCHIVE_INTEGER_H_INCLUDED +#define ARCHIVE_INTEGER_H_INCLUDED + +#include "archive_platform.h" + +/* Note: This is a purely internal header! */ +/* Do not use this outside of libarchive internal code! */ + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + +#ifdef HAVE_INTSAFE_H +#define ENABLE_INTSAFE_SIGNED_FUNCTIONS +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STDCKDINT_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifdef HAVE_STDCKDINT_H +#define USE_STDCKDINT 1 +#elif (__GNUC__ >= 5 && !defined(__INTEL_COMPILER)) +#define USE_BUILTIN 1 +#elif __has_builtin(__builtin_add_overflow) +#define USE_BUILTIN 1 +#elif defined HAVE_INTSAFE_H +#define USE_INTSAFE 1 +#endif + +/* + * Disabling inline keyword for compilers known to choke on it: + * - Watcom C++ in C code. (For any version?) + * - SGI MIPSpro + * - Microsoft Visual C++ 6.0 (supposedly newer versions too) + * - IBM VisualAge 6 (XL v6) + * - Sun WorkShop C (SunPro) before 5.9 + */ +#if defined(__WATCOMC__) || defined(__sgi) || defined(__hpux) || defined(__BORLANDC__) +#define inline +#elif defined(__IBMC__) && __IBMC__ < 700 +#define inline +#elif defined(__SUNPRO_C) && __SUNPRO_C < 0x590 +#define inline +#elif defined(_MSC_VER) || defined(__osf__) +#define inline __inline +#endif + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_add_i64(int64_t *result, int64_t a, int64_t b) +{ +#if USE_STDCKDINT + return ckd_add(result, a, b); +#elif USE_BUILTIN + return __builtin_add_overflow(a, b, result); +#elif USE_INTSAFE + LONGLONG res; + int ret; + + ret = LongLongAdd(a, b, &res); + *result = (int64_t)res; + return ret; +#else + if ((b > 0 && a > INT64_MAX - b) || + (b < 0 && a < INT64_MIN - b)) + return 1; + + *result = a + b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_add_size(size_t *result, size_t a, size_t b) +{ +#if USE_STDCKDINT + return ckd_add(result, a, b); +#elif USE_BUILTIN + return __builtin_add_overflow(a, b, result); +#elif USE_INTSAFE + return SizeTAdd(a, b, result); +#else + if (a > SIZE_MAX - b) + return 1; + *result = a + b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_add_u64(uint64_t *result, uint64_t a, uint64_t b) +{ +#if USE_STDCKDINT + return ckd_add(result, a, b); +#elif USE_BUILTIN + return __builtin_add_overflow(a, b, result); +#elif USE_INTSAFE + ULONGLONG res; + int ret; + + ret = ULongLongAdd(a, b, &res); + *result = (uint64_t)res; + return ret; +#else + if (a > UINT64_MAX - b) + return 1; + *result = a + b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_mul_i64(int64_t *result, int64_t a, int64_t b) +{ +#if USE_STDCKDINT + return ckd_mul(result, a, b); +#elif USE_BUILTIN + return __builtin_mul_overflow(a, b, result); +#elif USE_INTSAFE + LONGLONG res; + int ret; + + ret = LongLongMult(a, b, &res); + *result = (int64_t)res; + return ret; +#else + if ((a > 0 && b > 0 && a > INT64_MAX / b) || + (a < 0 && b > 0 && a < INT64_MIN / b) || + (a > 0 && b < 0 && b < INT64_MIN / a) || + (a < 0 && b < 0 && a < INT64_MAX / b)) + return 1; + + *result = a * b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_mul_size(size_t *result, size_t a, size_t b) +{ +#if USE_STDCKDINT + return ckd_mul(result, a, b); +#elif USE_BUILTIN + return __builtin_mul_overflow(a, b, result); +#elif USE_INTSAFE + return SizeTMult(a, b, result); +#else + if (b != 0 && a > SIZE_MAX / b) + return 1; + *result = a * b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_mul_u64(uint64_t *result, uint64_t a, uint64_t b) +{ +#if USE_STDCKDINT + return ckd_mul(result, a, b); +#elif USE_BUILTIN + return __builtin_mul_overflow(a, b, result); +#elif USE_INTSAFE + ULONGLONG res; + int ret; + + ret = ULongLongMult(a, b, &res); + *result = (uint64_t)res; + return ret; +#else + if (b != 0 && a > UINT64_MAX / b) + return 1; + *result = a * b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_sub_i64(int64_t *result, int64_t a, int64_t b) +{ +#if USE_STDCKDINT + return ckd_sub(result, a, b); +#elif USE_BUILTIN + return __builtin_sub_overflow(a, b, result); +#elif USE_INTSAFE + LONGLONG res; + int ret; + + ret = LongLongSub(a, b, &res); + *result = (int64_t)res; + return ret; +#else + if ((b > 0 && a < INT64_MIN + b) || + (b < 0 && a > INT64_MAX + b)) + return 1; + + *result = a - b; + return 0; +#endif +} + +#endif diff --git a/libarchive-clib/c/archive_match.c b/libarchive-clib/c/archive_match.c index 51a0e3f..ecd470b 100644 --- a/libarchive-clib/c/archive_match.c +++ b/libarchive-clib/c/archive_match.c @@ -40,6 +40,7 @@ #endif #include "archive.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_entry.h" #include "archive_pathmatch.h" @@ -216,6 +217,14 @@ error_nomem(struct archive_match *a) return (ARCHIVE_FATAL); } +static int +error_pattern(struct archive_match *a) +{ + archive_set_error(&(a->archive), EINVAL, "Failed to apply pattern"); + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); +} + /* * Create an ARCHIVE_MATCH object. */ @@ -269,7 +278,7 @@ archive_match_free(struct archive *_a) * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_excluded(struct archive *_a, struct archive_entry *entry) @@ -293,6 +302,8 @@ archive_match_excluded(struct archive *_a, struct archive_entry *entry) #else r = path_excluded(a, 1, archive_entry_pathname(entry)); #endif + if (r < 0) + return (error_pattern(a)); if (r != 0) return (r); } @@ -449,13 +460,14 @@ archive_match_include_pattern_from_file_w(struct archive *_a, * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_path_excluded(struct archive *_a, struct archive_entry *entry) { struct archive_match *a; + int r; archive_check_magic(_a, ARCHIVE_MATCH_MAGIC, ARCHIVE_STATE_NEW, "archive_match_path_excluded"); @@ -471,10 +483,13 @@ archive_match_path_excluded(struct archive *_a, if ((a->setflag & PATTERN_IS_SET) == 0) return (0); #if defined(_WIN32) && !defined(__CYGWIN__) - return (path_excluded(a, 0, archive_entry_pathname_w(entry))); + r = path_excluded(a, 0, archive_entry_pathname_w(entry)); #else - return (path_excluded(a, 1, archive_entry_pathname(entry))); + r = path_excluded(a, 1, archive_entry_pathname(entry)); #endif + if (r < 0) + return (error_pattern(a)); + return (r); } /* @@ -629,11 +644,12 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist, } r = archive_read_next_header(ar, &ae); if (r != ARCHIVE_OK) { - archive_read_free(ar); if (r == ARCHIVE_EOF) { + archive_read_free(ar); return (ARCHIVE_OK); } else { archive_copy_error(&(a->archive), ar); + archive_read_free(ar); return (r); } } @@ -1007,7 +1023,7 @@ archive_match_exclude_entry(struct archive *_a, int flag, * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_time_excluded(struct archive *_a, @@ -1653,7 +1669,7 @@ archive_match_include_gname_w(struct archive *_a, const wchar_t *gname) * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_owner_excluded(struct archive *_a, @@ -1684,15 +1700,22 @@ add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id) if (ids->count + 1 >= ids->size) { void *p; + size_t alloc_size, new_size; if (ids->size == 0) - ids->size = 8; - else - ids->size *= 2; - p = realloc(ids->ids, sizeof(*ids->ids) * ids->size); + new_size = 8; + else { + if (archive_ckd_mul_size(&new_size, ids->size, 2)) + return (error_nomem(a)); + } + if (archive_ckd_mul_size(&alloc_size, + new_size, sizeof(*ids->ids))) + return (error_nomem(a)); + p = realloc(ids->ids, alloc_size); if (p == NULL) return (error_nomem(a)); ids->ids = (int64_t *)p; + ids->size = new_size; } /* Find an insert point. */ diff --git a/libarchive-clib/c/archive_options.c b/libarchive-clib/c/archive_options.c index 66491bd..6190bb8 100644 --- a/libarchive-clib/c/archive_options.c +++ b/libarchive-clib/c/archive_options.c @@ -31,9 +31,9 @@ #include "archive_options_private.h" -static const char * -parse_option(const char **str, - const char **mod, const char **opt, const char **val); +static char * +parse_option(char **str, + char **mod, char **opt, char **val); int _archive_set_option(struct archive *a, @@ -103,7 +103,7 @@ _archive_set_options(struct archive *a, const char *options, { int allok = 1, anyok = 0, ignore_mod_err = 0, r; char *data; - const char *s, *mod, *opt, *val; + char *s, *mod, *opt, *val; archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn); @@ -115,7 +115,7 @@ _archive_set_options(struct archive *a, const char *options, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } - s = (const char *)data; + s = data; do { mod = opt = val = NULL; @@ -167,10 +167,10 @@ _archive_set_options(struct archive *a, const char *options, return allok ? ARCHIVE_OK : anyok ? ARCHIVE_WARN : ARCHIVE_FAILED; } -static const char * -parse_option(const char **s, const char **m, const char **o, const char **v) +static char * +parse_option(char **s, char **m, char **o, char **v) { - const char *end, *mod, *opt, *val; + char *end, *mod, *opt, *val; char *p; end = NULL; @@ -182,7 +182,7 @@ parse_option(const char **s, const char **m, const char **o, const char **v) if (p != NULL) { *p = '\0'; - end = ((const char *)p) + 1; + end = p + 1; } if (0 == strlen(opt)) { diff --git a/libarchive-clib/c/archive_pack_dev.c b/libarchive-clib/c/archive_pack_dev.c index 3c6209b..e293512 100644 --- a/libarchive-clib/c/archive_pack_dev.c +++ b/libarchive-clib/c/archive_pack_dev.c @@ -319,7 +319,7 @@ compare_format(const void *key, const void *element) pack_t * pack_find(const char *name) { - struct format *format; + const struct format *format; format = bsearch(name, formats, sizeof(formats)/sizeof(formats[0]), diff --git a/libarchive-clib/c/archive_parse_date.c b/libarchive-clib/c/archive_parse_date.c index d9e9683..c1b0f18 100644 --- a/libarchive-clib/c/archive_parse_date.c +++ b/libarchive-clib/c/archive_parse_date.c @@ -36,6 +36,7 @@ #include #include "archive.h" +#include "archive_integer.h" /* Basic time units. */ #define EPOCH 1970 @@ -49,7 +50,7 @@ enum DSTMODE { DSTon, DSToff, DSTmaybe }; enum { tAM, tPM }; /* Token types returned by nexttoken() */ enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT, - tUNUMBER, tZONE, tDST }; + tUNUMBER, tZONE, tDST, tERROR }; struct token { int token; time_t value; }; /* @@ -818,20 +819,33 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) } /* - * Parses and consumes an unsigned number. - * Returns 1 if any number is parsed. Otherwise, *value is unchanged. + * Parses and consumes an unsigned 64-bit number. + * Returns UINT64_MAX if the number overflows. */ -static char -consume_unsigned_number(const char **in, time_t *value) -{ - char c; - if (isdigit((unsigned char)(c = **in))) { - for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); ) - *value = 10 * *value + c - '0'; - (*in)--; - return 1; +static uint64_t +consume_unsigned_number(const char **in) { + uint64_t value = 0; + unsigned char c; + + /* Get the first character, abort if it's not a digit */ + c = (unsigned char)(**in); + if (c < '0' || c > '9') { + return UINT64_MAX; } - return 0; + + /* Fold digits into the value, abort on overflow */ + while (c >= '0' && c <= '9') { + unsigned char digit = c - '0'; + + /* Return error if the result would overflow UINT64_MAX */ + if (archive_ckd_mul_u64(&value, value, 10) || + archive_ckd_add_u64(&value, value, digit)) { + return UINT64_MAX; + } + (*in)++; + c = (unsigned char)(**in); + } + return value; } /* @@ -906,12 +920,19 @@ nexttoken(const char **in, time_t *value) } /* - * Not in the word table, maybe it's a number. Note: - * Because '-' and '+' have other special meanings, I - * don't deal with signed numbers here. + * Not in the word table. If it starts with a digit, + * it must be a number. Note: Because '-' and '+' have + * other special meanings, I don't deal with signed + * numbers here. */ - if (consume_unsigned_number(in, value)) { - return (tUNUMBER); + if (isdigit((unsigned char)(**in))) { + uint64_t val = consume_unsigned_number(in); + if (val > 9999) { + return (tERROR); + } else { + *value = val; + return (tUNUMBER); + } } return *(*in)++; @@ -949,6 +970,7 @@ difftm (struct tm *a, struct tm *b) static time_t parse_unix_epoch(const char *p) { + uint64_t val; time_t epoch; /* may begin with + */ @@ -957,12 +979,18 @@ parse_unix_epoch(const char *p) } /* followed by some number */ - if (!consume_unsigned_number(&p, &epoch)) + val = consume_unsigned_number(&p); + /* Truncate to time_t */ + epoch = (time_t)val; + /* If truncated value is different, then + * the value is too large for `time_t`. */ + if (epoch < 0 || (uint64_t)epoch != val) { return (time_t)-1; - - /* ...and nothing else */ - if (*p != '\0') + } + /* If there's any more characters, fail. */ + if (*p != '\0') { return (time_t)-1; + } return epoch; } diff --git a/libarchive-clib/c/archive_pathmatch.c b/libarchive-clib/c/archive_pathmatch.c index db0d2b7..4b69ae1 100644 --- a/libarchive-clib/c/archive_pathmatch.c +++ b/libarchive-clib/c/archive_pathmatch.c @@ -35,6 +35,8 @@ #include "archive_pathmatch.h" +#define MAX_RECURSION 100 + /* * Check whether a character 'c' is matched by a list specification [...]: * * Leading '!' or '^' negates the class. @@ -167,9 +169,13 @@ pm_slashskip_w(const wchar_t *s) { } static int -pm(const char *p, const char *s, int flags) +pm(const char *p, const char *s, int flags, int depth) { const char *end; + int r; + + if (depth > MAX_RECURSION) + return (-1); /* * Ignore leading './', './/', '././', etc. @@ -202,8 +208,9 @@ pm(const char *p, const char *s, int flags) if (*p == '\0') return (1); while (*s) { - if (pm(p, s, flags)) - return (1); + r = pm(p, s, flags, depth + 1); + if (r) + return (r); ++s; } return (0); @@ -220,7 +227,7 @@ pm(const char *p, const char *s, int flags) } if (*end == ']') { /* We found [...], try to match it. */ - if (!pm_list(p + 1, end, *s, flags)) + if (*s == '\0' || !pm_list(p + 1, end, *s, flags)) return (0); p = end; /* Jump to trailing ']' char. */ break; @@ -272,9 +279,13 @@ pm(const char *p, const char *s, int flags) } static int -pm_w(const wchar_t *p, const wchar_t *s, int flags) +pm_w(const wchar_t *p, const wchar_t *s, int flags, int depth) { const wchar_t *end; + int r; + + if (depth > MAX_RECURSION) + return (-1); /* * Ignore leading './', './/', '././', etc. @@ -307,8 +318,9 @@ pm_w(const wchar_t *p, const wchar_t *s, int flags) if (*p == L'\0') return (1); while (*s) { - if (pm_w(p, s, flags)) - return (1); + r = pm_w(p, s, flags, depth + 1); + if (r) + return (r); ++s; } return (0); @@ -387,7 +399,7 @@ __archive_pathmatch(const char *p, const char *s, int flags) return (0); /* Leading '^' anchors the start of the pattern. */ - if (*p == '^') { + if ((flags & PATHMATCH_NO_ANCHOR_START) && *p == '^') { ++p; flags &= ~PATHMATCH_NO_ANCHOR_START; } @@ -401,22 +413,25 @@ __archive_pathmatch(const char *p, const char *s, int flags) ++p; while (*s == '/') ++s; - return (pm(p, s, flags)); + return (pm(p, s, flags, 0)); } /* If start is unanchored, try to match start of each path element. */ if (flags & PATHMATCH_NO_ANCHOR_START) { for ( ; s != NULL; s = strchr(s, '/')) { + int r; + if (*s == '/') s++; - if (pm(p, s, flags)) - return (1); + r = pm(p, s, flags, 0); + if (r) + return (r); } return (0); } /* Default: Match from beginning. */ - return (pm(p, s, flags)); + return (pm(p, s, flags, 0)); } int @@ -429,7 +444,7 @@ __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags) return (0); /* Leading '^' anchors the start of the pattern. */ - if (*p == L'^') { + if ((flags & PATHMATCH_NO_ANCHOR_START) && *p == L'^') { ++p; flags &= ~PATHMATCH_NO_ANCHOR_START; } @@ -443,20 +458,23 @@ __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags) ++p; while (*s == L'/') ++s; - return (pm_w(p, s, flags)); + return (pm_w(p, s, flags, 0)); } /* If start is unanchored, try to match start of each path element. */ if (flags & PATHMATCH_NO_ANCHOR_START) { for ( ; s != NULL; s = wcschr(s, L'/')) { + int r; + if (*s == L'/') s++; - if (pm_w(p, s, flags)) - return (1); + r = pm_w(p, s, flags, 0); + if (r) + return (r); } return (0); } /* Default: Match from beginning. */ - return (pm_w(p, s, flags)); + return (pm_w(p, s, flags, 0)); } diff --git a/libarchive-clib/c/archive_random.c b/libarchive-clib/c/archive_random.c index 8c48d2d..28cba00 100644 --- a/libarchive-clib/c/archive_random.c +++ b/libarchive-clib/c/archive_random.c @@ -58,18 +58,12 @@ static void la_arc4random_buf(void *, size_t); #include "archive_random_private.h" #if defined(_WIN32) && !defined(__CYGWIN__) -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA -/* don't use bcrypt when XP needs to be supported */ #include /* Common in other bcrypt implementations, but missing from VS2008. */ #ifndef BCRYPT_SUCCESS #define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) #endif - -#elif defined(HAVE_WINCRYPT_H) -#include -#endif #endif #ifndef O_CLOEXEC @@ -85,7 +79,6 @@ int archive_random(void *buf, size_t nbytes) { #if defined(_WIN32) && !defined(__CYGWIN__) -# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA NTSTATUS status; BCRYPT_ALG_HANDLE hAlg; @@ -98,25 +91,6 @@ archive_random(void *buf, size_t nbytes) return ARCHIVE_FAILED; return ARCHIVE_OK; -# else - HCRYPTPROV hProv; - BOOL success; - - success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT); - if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) { - success = CryptAcquireContext(&hProv, NULL, NULL, - PROV_RSA_FULL, CRYPT_NEWKEYSET); - } - if (success) { - success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf); - CryptReleaseContext(hProv, 0); - if (success) - return ARCHIVE_OK; - } - /* TODO: Does this case really happen? */ - return ARCHIVE_FAILED; -# endif #elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) la_arc4random_buf(buf, nbytes); return ARCHIVE_OK; diff --git a/libarchive-clib/c/archive_read.c b/libarchive-clib/c/archive_read.c index e5f89bd..b23d99d 100644 --- a/libarchive-clib/c/archive_read.c +++ b/libarchive-clib/c/archive_read.c @@ -36,6 +36,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #include #ifdef HAVE_STDLIB_H #include @@ -49,6 +52,7 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -252,42 +256,39 @@ client_close_proxy(struct archive_read_filter *self) return read_client_close_proxy(self->archive); } -static int -client_open_proxy(struct archive_read_filter *self) -{ - int r = ARCHIVE_OK; - if (self->archive->client.opener != NULL) - r = (self->archive->client.opener)( - (struct archive *)self->archive, self->data); - return (r); -} - static int client_switch_proxy(struct archive_read_filter *self, unsigned int iindex) { - int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK; - void *data2 = NULL; + struct archive_read *a; + int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK; + void *data2; + + while (self->upstream != NULL) + self = self->upstream; + a = self->archive; /* Don't do anything if already in the specified data node */ - if (self->archive->client.cursor == iindex) + if (a->client.cursor == iindex) return (ARCHIVE_OK); - self->archive->client.cursor = iindex; - data2 = self->archive->client.dataset[self->archive->client.cursor].data; - if (self->archive->client.switcher != NULL) + a->client.cursor = iindex; + data2 = a->client.dataset[a->client.cursor].data; + if (a->client.switcher != NULL) { - r1 = r2 = (self->archive->client.switcher) - ((struct archive *)self->archive, self->data, data2); + r1 = r2 = (a->client.switcher) + ((struct archive *)a, self->data, data2); self->data = data2; } else { /* Attempt to call close and open instead */ - if (self->archive->client.closer != NULL) - r1 = (self->archive->client.closer) - ((struct archive *)self->archive, self->data); + if (a->client.closer != NULL) + r1 = (a->client.closer) + ((struct archive *)a, self->data); self->data = data2; - r2 = client_open_proxy(self); + if (a->client.opener != NULL) + r2 = (a->client.opener) + ((struct archive *)a, self->data); } return (r1 < r2) ? r1 : r2; } @@ -403,7 +404,9 @@ archive_read_add_callback_data(struct archive *_a, void *client_data, { struct archive_read *a = (struct archive_read *)_a; void *p; + size_t alloc_size; unsigned int i; + unsigned int nodes; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_add_callback_data"); @@ -412,14 +415,26 @@ archive_read_add_callback_data(struct archive *_a, void *client_data, "Invalid index specified"); return ARCHIVE_FATAL; } - p = realloc(a->client.dataset, sizeof(*a->client.dataset) - * (++(a->client.nodes))); + + if (a->client.nodes == UINT_MAX || + archive_ckd_mul_size(&alloc_size, + (size_t)a->client.nodes + 1, sizeof(*a->client.dataset))) { + archive_set_error(&a->archive, ENOMEM, + "No memory"); + return ARCHIVE_FATAL; + } + + nodes = a->client.nodes + 1; + p = realloc(a->client.dataset, alloc_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return ARCHIVE_FATAL; } + a->client.dataset = (struct archive_read_data_node *)p; + a->client.nodes = nodes; + for (i = a->client.nodes - 1; i > iindex; i--) { a->client.dataset[i].data = a->client.dataset[i-1].data; a->client.dataset[i].begin_position = -1; @@ -934,7 +949,10 @@ archive_read_data_skip(struct archive *_a) if (r == ARCHIVE_EOF) r = ARCHIVE_OK; - a->archive.state = ARCHIVE_STATE_HEADER; + if (r == ARCHIVE_FATAL) + a->archive.state = ARCHIVE_STATE_FATAL; + else + a->archive.state = ARCHIVE_STATE_HEADER; return (r); } @@ -942,6 +960,8 @@ la_int64_t archive_seek_data(struct archive *_a, int64_t offset, int whence) { struct archive_read *a = (struct archive_read *)_a; + la_int64_t r; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_seek_data_block"); @@ -949,10 +969,14 @@ archive_seek_data(struct archive *_a, int64_t offset, int whence) archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: " "No format_seek_data_block function registered"); + a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } - return (a->format->seek_data)(a, offset, whence); + r = (a->format->seek_data)(a, offset, whence); + if (r == ARCHIVE_FATAL) + a->archive.state = ARCHIVE_STATE_FATAL; + return (r); } /* @@ -968,6 +992,8 @@ _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read *a = (struct archive_read *)_a; + int r; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); @@ -975,10 +1001,14 @@ _archive_read_data_block(struct archive *_a, archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: " "No format->read_data function registered"); + a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } - return (a->format->read_data)(a, buff, size, offset); + r = (a->format->read_data)(a, buff, size, offset); + if (r == ARCHIVE_FATAL) + a->archive.state = ARCHIVE_STATE_FATAL; + return (r); } static int @@ -1366,7 +1396,7 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, /* Move data forward in copy buffer if necessary. */ if (filter->next > filter->buffer && - filter->next + min > filter->buffer + filter->buffer_size) { + min > filter->buffer_size - (filter->next - filter->buffer)) { if (filter->avail > 0) memmove(filter->buffer, filter->next, filter->avail); diff --git a/libarchive-clib/c/archive_read_append_filter.c b/libarchive-clib/c/archive_read_append_filter.c index d578b06..04c3627 100644 --- a/libarchive-clib/c/archive_read_append_filter.c +++ b/libarchive-clib/c/archive_read_append_filter.c @@ -37,7 +37,7 @@ int archive_read_append_filter(struct archive *_a, int code) { int r1, r2, number_bidders, i; - char str[20]; + const char *str; struct archive_read_filter_bidder *bidder; struct archive_read_filter *filter; struct archive_read *a = (struct archive_read *)_a; @@ -53,15 +53,15 @@ archive_read_append_filter(struct archive *_a, int code) r1 = (ARCHIVE_OK); break; case ARCHIVE_FILTER_GZIP: - strcpy(str, "gzip"); + str = "gzip"; r1 = archive_read_support_filter_gzip(_a); break; case ARCHIVE_FILTER_BZIP2: - strcpy(str, "bzip2"); + str = "bzip2"; r1 = archive_read_support_filter_bzip2(_a); break; case ARCHIVE_FILTER_COMPRESS: - strcpy(str, "compress (.Z)"); + str = "compress (.Z)"; r1 = archive_read_support_filter_compress(_a); break; case ARCHIVE_FILTER_PROGRAM: @@ -69,43 +69,43 @@ archive_read_append_filter(struct archive *_a, int code) "Cannot append program filter using archive_read_append_filter"); return (ARCHIVE_FATAL); case ARCHIVE_FILTER_LZMA: - strcpy(str, "lzma"); + str = "lzma"; r1 = archive_read_support_filter_lzma(_a); break; case ARCHIVE_FILTER_XZ: - strcpy(str, "xz"); + str = "xz"; r1 = archive_read_support_filter_xz(_a); break; case ARCHIVE_FILTER_UU: - strcpy(str, "uu"); + str = "uu"; r1 = archive_read_support_filter_uu(_a); break; case ARCHIVE_FILTER_RPM: - strcpy(str, "rpm"); + str = "rpm"; r1 = archive_read_support_filter_rpm(_a); break; case ARCHIVE_FILTER_LZ4: - strcpy(str, "lz4"); + str = "lz4"; r1 = archive_read_support_filter_lz4(_a); break; case ARCHIVE_FILTER_ZSTD: - strcpy(str, "zstd"); + str = "zstd"; r1 = archive_read_support_filter_zstd(_a); break; case ARCHIVE_FILTER_LZIP: - strcpy(str, "lzip"); + str = "lzip"; r1 = archive_read_support_filter_lzip(_a); break; case ARCHIVE_FILTER_LZOP: - strcpy(str, "lzop"); + str = "lzop"; r1 = archive_read_support_filter_lzop(_a); break; case ARCHIVE_FILTER_LRZIP: - strcpy(str, "lrzip"); + str = "lrzip"; r1 = archive_read_support_filter_lrzip(_a); break; case ARCHIVE_FILTER_GRZIP: - strcpy(str, "grzip"); + str = "grzip"; r1 = archive_read_support_filter_grzip(_a); break; default: diff --git a/libarchive-clib/c/archive_read_data_into_fd.c b/libarchive-clib/c/archive_read_data_into_fd.c index 8fd5e12..38de5b4 100644 --- a/libarchive-clib/c/archive_read_data_into_fd.c +++ b/libarchive-clib/c/archive_read_data_into_fd.c @@ -46,9 +46,9 @@ */ static int pad_to(struct archive *a, int fd, int can_lseek, - size_t nulls_size, const char *nulls, - int64_t target_offset, int64_t actual_offset) + char **nulls, int64_t target_offset, int64_t actual_offset) { + const size_t nulls_size = 16384; size_t to_write; ssize_t bytes_written; @@ -56,16 +56,26 @@ pad_to(struct archive *a, int fd, int can_lseek, actual_offset = lseek(fd, target_offset - actual_offset, SEEK_CUR); if (actual_offset != target_offset) { - archive_set_error(a, errno, "Seek error"); + archive_set_error(a, + actual_offset == -1 ? errno : ARCHIVE_ERRNO_MISC, + "Seek error"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } + if (*nulls == NULL) { + *nulls = calloc(1, nulls_size); + if (*nulls == NULL) { + archive_set_error(a, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + } + while (target_offset > actual_offset) { to_write = nulls_size; if (target_offset < actual_offset + (int64_t)nulls_size) to_write = (size_t)(target_offset - actual_offset); - bytes_written = write(fd, nulls, to_write); + bytes_written = write(fd, *nulls, to_write); if (bytes_written < 0) { archive_set_error(a, errno, "Write error"); return (ARCHIVE_FATAL); @@ -84,29 +94,27 @@ archive_read_data_into_fd(struct archive *a, int fd) const void *buff; size_t size, bytes_to_write; ssize_t bytes_written; + int64_t fd_offset; int64_t target_offset; int64_t actual_offset = 0; int can_lseek; char *nulls = NULL; - size_t nulls_size = 16384; archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd"); can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode); - if (!can_lseek) { - nulls = calloc(1, nulls_size); - if (!nulls) { - r = ARCHIVE_FATAL; - goto cleanup; - } + if (can_lseek) { + fd_offset = lseek(fd, 0, SEEK_CUR); + if (fd_offset == -1) + can_lseek = 0; } while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) == ARCHIVE_OK) { const char *p = buff; if (target_offset > actual_offset) { - r = pad_to(a, fd, can_lseek, nulls_size, nulls, + r = pad_to(a, fd, can_lseek, &nulls, target_offset, actual_offset); if (r != ARCHIVE_OK) break; @@ -129,7 +137,7 @@ archive_read_data_into_fd(struct archive *a, int fd) } if (r == ARCHIVE_EOF && target_offset > actual_offset) { - r2 = pad_to(a, fd, can_lseek, nulls_size, nulls, + r2 = pad_to(a, fd, can_lseek, &nulls, target_offset, actual_offset); if (r2 != ARCHIVE_OK) r = r2; @@ -139,5 +147,15 @@ archive_read_data_into_fd(struct archive *a, int fd) free(nulls); if (r != ARCHIVE_EOF) return (r); - return (ARCHIVE_OK); + r = ARCHIVE_OK; + if (can_lseek) { + int64_t offset = lseek(fd, 0, SEEK_CUR); + if (offset - fd_offset != actual_offset) { + archive_set_error(a, + offset == -1 ? errno : ARCHIVE_ERRNO_MISC, + "Seek error"); + r = ARCHIVE_FATAL; + } + } + return (r); } diff --git a/libarchive-clib/c/archive_read_disk_entry_from_file.c b/libarchive-clib/c/archive_read_disk_entry_from_file.c index 6e6bae6..c4ad595 100644 --- a/libarchive-clib/c/archive_read_disk_entry_from_file.c +++ b/libarchive-clib/c/archive_read_disk_entry_from_file.c @@ -256,6 +256,11 @@ archive_read_disk_entry_from_file(struct archive *_a, char *linkbuffer; ssize_t lnklen; + if (st->st_size >= SSIZE_MAX) { + archive_set_error(&a->archive, ENOMEM, + "Couldn't read link data"); + return (ARCHIVE_FAILED); + } linkbuffer = malloc(linkbuffer_len + 1); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -894,7 +899,7 @@ setup_sparse_fiemap(struct archive_read_disk *a, r = ioctl(*fd, FS_IOC_FIEMAP, fm); if (r < 0) { - /* When something error happens, it is better we + /* When some error happens, it is better we * should return ARCHIVE_OK because an earlier * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ goto exit_setup_sparse_fiemap; diff --git a/libarchive-clib/c/archive_read_disk_posix.c b/libarchive-clib/c/archive_read_disk_posix.c index a7176bd..47c1d33 100644 --- a/libarchive-clib/c/archive_read_disk_posix.c +++ b/libarchive-clib/c/archive_read_disk_posix.c @@ -920,7 +920,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, + archive_set_error(&(a->archive), archive_errno(a->matching), "%s", archive_error_string(a->matching)); return (r); } @@ -1034,7 +1034,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, + archive_set_error(&(a->archive), archive_errno(a->matching), "%s", archive_error_string(a->matching)); return (r); } @@ -1060,7 +1060,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, + archive_set_error(&(a->archive), archive_errno(a->matching), "%s", archive_error_string(a->matching)); return (r); } @@ -1413,18 +1413,18 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) /* * This is the new filesystem which we have to generate a new ID for. */ - fid = t->max_filesystem_id++; - if (fid > MAX_FILESYSTEM_ID) { + fid = t->max_filesystem_id; + if (fid >= MAX_FILESYSTEM_ID) { archive_set_error(&a->archive, ENOMEM, "Too many filesystems"); return (ARCHIVE_FATAL); } - if (t->max_filesystem_id > t->allocated_filesystem) { + if (fid + 1 > t->allocated_filesystem) { int s; void *p; - s = t->max_filesystem_id * 2; + s = (fid + 1) * 2; p = realloc(t->filesystem_table, - s * sizeof(*t->filesystem_table)); + s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); @@ -1433,6 +1433,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } + t->max_filesystem_id = fid + 1; t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; diff --git a/libarchive-clib/c/archive_read_disk_windows.c b/libarchive-clib/c/archive_read_disk_windows.c index 0e89049..d5f7ce7 100644 --- a/libarchive-clib/c/archive_read_disk_windows.c +++ b/libarchive-clib/c/archive_read_disk_windows.c @@ -358,6 +358,8 @@ la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype) } indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (indata == NULL) + return (-1); ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, 1024, &inbytes, NULL); if (ret == 0) { @@ -946,7 +948,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, + archive_set_error(&(a->archive), archive_errno(a->matching), "%s", archive_error_string(a->matching)); return (r); } @@ -1018,7 +1020,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, + archive_set_error(&(a->archive), archive_errno(a->matching), "%s", archive_error_string(a->matching)); return (r); } @@ -1044,7 +1046,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, + archive_set_error(&(a->archive), archive_errno(a->matching), "%s", archive_error_string(a->matching)); return (r); } @@ -1450,18 +1452,18 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) /* * There is a new filesystem, we generate a new ID for. */ - fid = t->max_filesystem_id++; - if (fid > MAX_FILESYSTEM_ID) { + fid = t->max_filesystem_id; + if (fid >= MAX_FILESYSTEM_ID) { archive_set_error(&a->archive, ENOMEM, "Too many filesystems"); return (ARCHIVE_FATAL); } - if (t->max_filesystem_id > t->allocated_filesystem) { + if (fid + 1 > t->allocated_filesystem) { int s; void *p; - s = t->max_filesystem_id * 2; + s = (fid + 1) * 2; p = realloc(t->filesystem_table, - s * sizeof(*t->filesystem_table)); + s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); @@ -1470,6 +1472,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } + t->max_filesystem_id = fid + 1; t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; @@ -1632,6 +1635,8 @@ tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path, struct tree_entry *te; te = calloc(1, sizeof(*te)); + if (te == NULL) + return; te->next = t->stack; te->parent = t->current; if (te->parent) @@ -1704,6 +1709,8 @@ tree_open(const wchar_t *path, int symlink_mode, int restore_time) struct tree *t; t = calloc(1, sizeof(*t)); + if (t == NULL) + return (NULL); archive_string_init(&(t->full_path)); archive_string_init(&t->path); if (archive_wstring_ensure(&t->path, 15) == NULL) { diff --git a/libarchive-clib/c/archive_read_open_filename.c b/libarchive-clib/c/archive_read_open_filename.c index 9ec1e6c..91f64b6 100644 --- a/libarchive-clib/c/archive_read_open_filename.c +++ b/libarchive-clib/c/archive_read_open_filename.c @@ -40,6 +40,9 @@ #ifdef HAVE_IO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif @@ -181,7 +184,7 @@ archive_read_open_filenames_w(struct archive *a, const wchar_t **wfilenames, if (wfilename == NULL) wfilename = L""; mine = calloc(1, - sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); + sizeof(*mine) + wcslen(wfilename) * MB_LEN_MAX); if (mine == NULL) goto no_memory; mine->block_size = block_size; @@ -315,7 +318,7 @@ file_open(struct archive *a, void *client_data) } #else archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Unexpedted operation in archive_read_open_filename"); + "Unexpected operation in archive_read_open_filename"); goto fail; #endif } diff --git a/libarchive-clib/c/archive_read_support_filter_all.c b/libarchive-clib/c/archive_read_support_filter_all.c index cb46d12..f086773 100644 --- a/libarchive-clib/c/archive_read_support_filter_all.c +++ b/libarchive-clib/c/archive_read_support_filter_all.c @@ -43,34 +43,34 @@ archive_read_support_filter_all(struct archive *a) archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_filter_all"); - /* Bzip falls back to "bunzip2" command-line */ + /* Bzip falls back to "bzip2 -d" command-line */ archive_read_support_filter_bzip2(a); /* The decompress code doesn't use an outside library. */ archive_read_support_filter_compress(a); /* Gzip decompress falls back to "gzip -d" command-line. */ archive_read_support_filter_gzip(a); - /* Lzip falls back to "unlzip" command-line program. */ + /* Lzip falls back to "lzip -d -q" command-line. */ archive_read_support_filter_lzip(a); /* The LZMA file format has a very weak signature, so it * may not be feasible to keep this here, but we'll try. * This will come back out if there are problems. */ - /* Lzma falls back to "unlzma" command-line program. */ + /* Lzma falls back to "lzma -d -qq" command-line. */ archive_read_support_filter_lzma(a); - /* Xz falls back to "unxz" command-line program. */ + /* Xz falls back to "xz -d -qq" command-line. */ archive_read_support_filter_xz(a); /* The decode code doesn't use an outside library. */ archive_read_support_filter_uu(a); /* The decode code doesn't use an outside library. */ archive_read_support_filter_rpm(a); - /* The decode code always uses "lrzip -q -d" command-line. */ + /* The decode code always uses "lrzip -d -q" command-line. */ archive_read_support_filter_lrzip(a); /* Lzop decompress falls back to "lzop -d" command-line. */ archive_read_support_filter_lzop(a); /* The decode code always uses "grzip -d" command-line. */ archive_read_support_filter_grzip(a); - /* Lz4 falls back to "lz4 -d" command-line program. */ + /* Lz4 falls back to "lz4 -d -q" command-line. */ archive_read_support_filter_lz4(a); - /* Zstd falls back to "zstd -d" command-line program. */ + /* Zstd falls back to "zstd -d -qq" command-line. */ archive_read_support_filter_zstd(a); /* Note: We always return ARCHIVE_OK here, even if some of the diff --git a/libarchive-clib/c/archive_read_support_filter_bzip2.c b/libarchive-clib/c/archive_read_support_filter_bzip2.c index 479237c..26d550e 100644 --- a/libarchive-clib/c/archive_read_support_filter_bzip2.c +++ b/libarchive-clib/c/archive_read_support_filter_bzip2.c @@ -28,6 +28,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #include #ifdef HAVE_STDLIB_H #include @@ -63,8 +66,7 @@ static int bzip2_filter_close(struct archive_read_filter *); /* * Note that we can detect bzip2 archives even if we can't decompress * them. (In fact, we like detecting them because we can give better - * error messages.) So the bid framework here gets compiled even - * if bzlib is unavailable. + * error messages.) */ static int bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int bzip2_reader_init(struct archive_read_filter *); @@ -113,13 +115,12 @@ static int bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; - ssize_t avail; int bits_checked; (void)self; /* UNUSED */ /* Minimal bzip2 archive is 14 bytes. */ - buffer = __archive_read_filter_ahead(filter, 14, &avail); + buffer = __archive_read_filter_ahead(filter, 14, NULL); if (buffer == NULL) return (0); @@ -150,9 +151,9 @@ bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_fi #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) /* - * If we don't have the library on this system, we can't actually do the - * decompression. We can, however, still detect compressed archives - * and emit a useful message. + * If we don't have the library on this system, we can't do the + * decompression directly. We can, however, try to run "bzip2 -d" + * in case that's available. */ static int bzip2_reader_init(struct archive_read_filter *self) @@ -232,6 +233,8 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) /* Try to fill the output buffer. */ for (;;) { + ssize_t max_in; + if (!state->valid) { if (bzip2_reader_bid(self->bidder, self->upstream) == 0) { state->eof = 1; @@ -286,15 +289,13 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) return (ARCHIVE_FATAL); } state->stream.next_in = (char *)(uintptr_t)read_buf; + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (ret > max_in) + ret = max_in; state->stream.avail_in = (uint32_t)ret; - /* There is no more data, return whatever we have. */ - if (ret == 0) { - state->eof = 1; - *p = state->out_block; - decompressed = state->stream.next_out - - state->out_block; - return (decompressed); - } /* Decompress as much as we can in one pass. */ ret = BZ2_bzDecompress(&(state->stream)); diff --git a/libarchive-clib/c/archive_read_support_filter_compress.c b/libarchive-clib/c/archive_read_support_filter_compress.c index b89eaab..54a9981 100644 --- a/libarchive-clib/c/archive_read_support_filter_compress.c +++ b/libarchive-clib/c/archive_read_support_filter_compress.c @@ -104,6 +104,7 @@ struct private_data { void *out_block; /* Decompression status variables. */ + int initialized; int use_reset_code; int end_of_stream; /* EOF status. */ int maxcode; /* Largest code. */ @@ -172,13 +173,12 @@ compress_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; - ssize_t avail; int bits_checked; (void)self; /* UNUSED */ /* Shortest valid compress file is 3 bytes. */ - buffer = __archive_read_filter_ahead(filter, 3, &avail); + buffer = __archive_read_filter_ahead(filter, 3, NULL); if (buffer == NULL) return (0); @@ -212,7 +212,6 @@ compress_bidder_init(struct archive_read_filter *self) struct private_data *state; static const size_t out_block_size = 64 * 1024; void *out_block; - int code; self->code = ARCHIVE_FILTER_COMPRESS; self->name = "compress (.Z)"; @@ -233,14 +232,23 @@ compress_bidder_init(struct archive_read_filter *self) state->out_block = out_block; self->vtable = &compress_reader_vtable; - /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */ + return (ARCHIVE_OK); +} + +static int +compress_filter_init(struct archive_read_filter *self) +{ + struct private_data *state = (struct private_data *)self->data; + int code; + + state->initialized = 1; (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) { + if (code < 0 || (code & 0x1f) > 16) { archive_set_error(&self->archive->archive, -1, "Invalid compressed data"); return (ARCHIVE_FATAL); @@ -278,6 +286,11 @@ compress_filter_read(struct archive_read_filter *self, const void **pblock) int ret; state = (struct private_data *)self->data; + if (!state->initialized) { + ret = compress_filter_init(self); + if (ret != ARCHIVE_OK) + return (ret); + } if (state->end_of_stream) { *pblock = NULL; return (0); @@ -325,18 +338,11 @@ next_code(struct archive_read_filter *self) struct private_data *state = (struct private_data *)self->data; int code, newcode; - static int debug_buff[1024]; - static unsigned debug_index; - again: code = newcode = getbits(self, state->bits); if (code < 0) return (code); - debug_buff[debug_index++] = code; - if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0])) - debug_index = 0; - /* If it's a reset code, reset the dictionary. */ if ((code == 256) && state->use_reset_code) { /* @@ -434,7 +440,7 @@ getbits(struct archive_read_filter *self, int n) 1, &ret); if (ret == 0) return (-1); - if (ret < 0 || state->next_in == NULL) + if (state->next_in == NULL) return (ARCHIVE_FATAL); state->consume_unnotified = state->avail_in = ret; } diff --git a/libarchive-clib/c/archive_read_support_filter_grzip.c b/libarchive-clib/c/archive_read_support_filter_grzip.c index 8ec5d1f..2bbb2e0 100644 --- a/libarchive-clib/c/archive_read_support_filter_grzip.c +++ b/libarchive-clib/c/archive_read_support_filter_grzip.c @@ -80,12 +80,11 @@ grzip_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *p; - ssize_t avail; (void)self; /* UNUSED */ - p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), NULL); + if (p == NULL) return (0); if (memcmp(p, grzip_magic, sizeof(grzip_magic))) diff --git a/libarchive-clib/c/archive_read_support_filter_gzip.c b/libarchive-clib/c/archive_read_support_filter_gzip.c index 726385e..a312065 100644 --- a/libarchive-clib/c/archive_read_support_filter_gzip.c +++ b/libarchive-clib/c/archive_read_support_filter_gzip.c @@ -56,7 +56,6 @@ struct private_data { char in_stream; unsigned char *out_block; size_t out_block_size; - int64_t total_out; unsigned long crc; uint32_t mtime; char *name; @@ -71,12 +70,7 @@ static int gzip_filter_close(struct archive_read_filter *); /* * Note that we can detect gzip archives even if we can't decompress * them. (In fact, we like detecting them because we can give better - * error messages.) So the bid framework here gets compiled even - * if zlib is unavailable. - * - * TODO: If zlib is unavailable, gzip_bidder_init() should - * use the compress_program framework to try to fire up an external - * gzip program. + * error messages.) */ static int gzip_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -146,7 +140,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, * is all fixed layout. */ len = 10; p = __archive_read_filter_ahead(filter, len, &avail); - if (p == NULL || avail == 0) + if (p == NULL) return (0); /* We only support deflation- third byte must be 0x08. */ if (memcmp(p, "\x1F\x8B\x08", 3) != 0) @@ -171,7 +165,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, p = __archive_read_filter_ahead(filter, len + 2, &avail); if (p == NULL) return (0); - len += ((int)p[len + 1] << 8) | (int)p[len]; + len += archive_le16dec(p + len); len += 2; } @@ -284,7 +278,7 @@ gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry) state = (struct private_data *)self->data; - /* A mtime of 0 is considered invalid/missing. */ + /* An mtime of 0 is considered invalid/missing. */ if (state->mtime != 0) archive_entry_set_mtime(entry, state->mtime, 0); @@ -341,7 +335,7 @@ static int consume_header(struct archive_read_filter *self) { struct private_data *state; - ssize_t avail; + ssize_t avail, max_in; size_t len; int ret; @@ -359,6 +353,18 @@ consume_header(struct archive_read_filter *self) /* Initialize compression library. */ state->stream.next_in = (unsigned char *)(uintptr_t) __archive_read_filter_ahead(self->upstream, 1, &avail); + if (avail < 0) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Failed to read gzip input"); + return (ARCHIVE_FATAL); + } + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (avail > max_in) + avail = max_in; state->stream.avail_in = (uInt)avail; ret = inflateInit2(&(state->stream), -15 /* Don't check for zlib header */); @@ -400,7 +406,6 @@ consume_trailer(struct archive_read_filter *self) { struct private_data *state; const unsigned char *p; - ssize_t avail; state = (struct private_data *)self->data; @@ -416,8 +421,8 @@ consume_trailer(struct archive_read_filter *self) } /* GZip trailer is a fixed 8 byte structure. */ - p = __archive_read_filter_ahead(self->upstream, 8, &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(self->upstream, 8, NULL); + if (p == NULL) return (ARCHIVE_FATAL); /* XXX TODO: Verify the length and CRC. */ @@ -502,7 +507,6 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) /* We've read as much as we can. */ decompressed = state->stream.next_out - state->out_block; - state->total_out += decompressed; if (decompressed == 0) *p = NULL; else diff --git a/libarchive-clib/c/archive_read_support_filter_lrzip.c b/libarchive-clib/c/archive_read_support_filter_lrzip.c index a562d53..9d51837 100644 --- a/libarchive-clib/c/archive_read_support_filter_lrzip.c +++ b/libarchive-clib/c/archive_read_support_filter_lrzip.c @@ -79,15 +79,15 @@ lrzip_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *p; - ssize_t avail, len; + ssize_t len; int i; (void)self; /* UNUSED */ /* Start by looking at the first six bytes of the header, which * is all fixed layout. */ len = 6; - p = __archive_read_filter_ahead(filter, len, &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(filter, len, NULL); + if (p == NULL) return (0); if (memcmp(p, LRZIP_HEADER_MAGIC, LRZIP_HEADER_MAGIC_LEN)) diff --git a/libarchive-clib/c/archive_read_support_filter_lz4.c b/libarchive-clib/c/archive_read_support_filter_lz4.c index acd7f51..2e5f634 100644 --- a/libarchive-clib/c/archive_read_support_filter_lz4.c +++ b/libarchive-clib/c/archive_read_support_filter_lz4.c @@ -44,6 +44,7 @@ #include "archive.h" #include "archive_endian.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_xxhash.h" @@ -95,8 +96,7 @@ static int lz4_filter_close(struct archive_read_filter *); /* * Note that we can detect lz4 archives even if we can't decompress * them. (In fact, we like detecting them because we can give better - * error messages.) So the bid framework here gets compiled even - * if liblz4 is unavailable. + * error messages.) */ static int lz4_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int lz4_reader_init(struct archive_read_filter *); @@ -145,52 +145,75 @@ lz4_reader_bid(struct archive_read_filter_bidder *self, const unsigned char *buffer; ssize_t avail; int bits_checked = 0; - ssize_t min_lz4_archive_size = 11; + const size_t min_lz4_archive_size = 11; - // LZ4 skippable frames contain a 4 byte magic number followed by - // a 4 byte frame data size, then that number of bytes of data. Regular - // frames contain a 4 byte magic number followed by a 2-14 byte frame - // header, some data, and a 3 byte end marker. - ssize_t min_lz4_frame_size = 8; + /* + * LZ4 skippable frames contain a 4 byte magic number followed by + * a 4 byte frame data size, then that number of bytes of data. + * Regular frames contain a 4 byte magic number followed by a 2-14 + * byte frame header, some data, and a 3 byte end marker. + */ + const size_t min_lz4_frame_size = 8; - ssize_t offset_in_buffer = 0; - ssize_t max_lookahead = 64 * 1024; + size_t offset_in_buffer = 0; + const size_t max_lookahead = 64 * 1024; + uint32_t magic_number; - (void)self; // UNUSED + (void)self; /* UNUSED */ - // Zstd and LZ4 skippable frame magic numbers are identical. To - // differentiate these two, we need to look for a non-skippable - // frame. + /* + * Zstd and LZ4 skippable frame magic numbers are identical. To + * differentiate these two, we need to look for a non-skippable + * frame. + */ - // Minimal lz4 archive is 11 bytes. - buffer = __archive_read_filter_ahead(filter, min_lz4_archive_size, &avail); + /* Minimal lz4 archive is 11 bytes. */ + buffer = __archive_read_filter_ahead(filter, min_lz4_archive_size, + &avail); if (buffer == NULL) return (0); - uint32_t magic_number = archive_le32dec(buffer); + magic_number = archive_le32dec(buffer); while ((magic_number & LZ4_SKIPPABLE_MASK) == LZ4_SKIPPABLE_START) { + size_t min; + uint32_t frame_data_size; - offset_in_buffer += 4; // Skip over the magic number + /* Skip over the magic number */ + offset_in_buffer += 4; - // Ensure that we can read another 4 bytes. - if (offset_in_buffer + 4 > avail) { - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + 4, &avail); + /* Ensure that we can read another 4 bytes. */ + if (offset_in_buffer + 4 > (size_t)avail) { + buffer = __archive_read_filter_ahead(filter, + offset_in_buffer + 4, &avail); if (buffer == NULL) return (0); } - uint32_t frame_data_size = archive_le32dec(buffer + offset_in_buffer); + frame_data_size = archive_le32dec(buffer + offset_in_buffer); - // Skip over the 4 frame data size bytes, plus the value stored there. - offset_in_buffer += 4 + frame_data_size; + /* Skip over the 4 frame data size bytes */ + offset_in_buffer += 4; - // There should be at least one more frame if this is LZ4 data. - if (offset_in_buffer + min_lz4_frame_size > avail) { // TODO: should this be >= ? - if (offset_in_buffer + min_lz4_frame_size > max_lookahead) + /* Skip over the value stored there. */ + if (archive_ckd_add_size(&offset_in_buffer, + offset_in_buffer, frame_data_size)) + return (0); + + /* + * There should be at least one more frame + * if this is LZ4 data. + */ + if (archive_ckd_add_size(&min, + offset_in_buffer, min_lz4_frame_size)) + return (0); + /* TODO: should this be >= ? */ + if (min > (size_t)avail) { + if (min > max_lookahead) return (0); - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + min_lz4_frame_size, &avail); + buffer = __archive_read_filter_ahead(filter, + min, &avail); if (buffer == NULL) return (0); } @@ -198,8 +221,10 @@ lz4_reader_bid(struct archive_read_filter_bidder *self, magic_number = archive_le32dec(buffer + offset_in_buffer); } - // We have skipped over any skippable frames. Either a regular LZ4 frame - // follows, or this isn't LZ4 data. + /* + * We have skipped over any skippable frames. Either a regular LZ4 frame + * follows, or this isn't LZ4 data. + */ bits_checked = offset_in_buffer; buffer = buffer + offset_in_buffer; @@ -241,9 +266,9 @@ lz4_reader_bid(struct archive_read_filter_bidder *self, #if !defined(HAVE_LIBLZ4) /* - * If we don't have the library on this system, we can't actually do the - * decompression. We can, however, still detect compressed archives - * and emit a useful message. + * If we don't have the library on this system, we can't do the + * decompression directly. We can, however, try to run "lz4 -d -q" + * in case that's available. */ static int lz4_reader_init(struct archive_read_filter *self) @@ -304,13 +329,15 @@ lz4_allocate_out_block(struct archive_read_filter *self) out_block_size += 64 * 1024; if (state->out_block_size < out_block_size) { free(state->out_block); + state->out_block = NULL; out_block = malloc(out_block_size); - state->out_block_size = out_block_size; if (out_block == NULL) { + state->out_block_size = 0; archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for lz4 decompression"); return (ARCHIVE_FATAL); } + state->out_block_size = out_block_size; state->out_block = out_block; } if (!state->flags.block_independence) @@ -327,13 +354,15 @@ lz4_allocate_out_block_for_legacy(struct archive_read_filter *self) if (state->out_block_size < out_block_size) { free(state->out_block); + state->out_block = NULL; out_block = malloc(out_block_size); - state->out_block_size = out_block_size; if (out_block == NULL) { + state->out_block_size = 0; archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for lz4 decompression"); return (ARCHIVE_FATAL); } + state->out_block_size = out_block_size; state->out_block = out_block; } return (ARCHIVE_OK); @@ -361,7 +390,7 @@ lz4_filter_read(struct archive_read_filter *self, const void **p) break; case READ_DEFAULT_STREAM: case READ_LEGACY_STREAM: - /* Reading a lz4 stream already failed. */ + /* Reading an lz4 stream already failed. */ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Invalid sequence"); return (ARCHIVE_FATAL); @@ -408,9 +437,15 @@ lz4_filter_read(struct archive_read_filter *self, const void **p) "Malformed lz4 data"); return (ARCHIVE_FATAL); } - uint32_t skip_bytes = archive_le32dec(read_buf); - __archive_read_filter_consume(self->upstream, - 4 + skip_bytes); + int64_t skip_bytes = archive_le32dec(read_buf); + if (__archive_read_filter_consume(self->upstream, + 4 + skip_bytes) < 0) { + archive_set_error( + &self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Malformed lz4 data"); + return (ARCHIVE_FATAL); + } } else { /* Ignore following unrecognized data. */ state->eof = 1; @@ -512,8 +547,11 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) /* Make sure we have a large enough buffer for uncompressed data. */ if (lz4_allocate_out_block(self) != ARCHIVE_OK) return (ARCHIVE_FATAL); - if (state->flags.stream_checksum) + if (state->flags.stream_checksum) { state->xxh32_state = __archive_xxhash.XXH32_init(0); + if (state->xxh32_state == NULL) + return (ARCHIVE_FATAL); + } state->decoded_size = 0; /* Success */ @@ -530,7 +568,6 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) struct private_data *state = (struct private_data *)self->data; ssize_t compressed_size; const char *read_buf; - ssize_t bytes_remaining; int checksum_size; ssize_t uncompressed_size; size_t prefix64k; @@ -538,8 +575,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) *p = NULL; /* Make sure we have 4 bytes for a block size. */ - read_buf = __archive_read_filter_ahead(self->upstream, 4, - &bytes_remaining); + read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL); if (read_buf == NULL) goto truncated_error; compressed_size = archive_le32dec(read_buf); @@ -565,7 +601,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) a huge buffer used for decoded data. */ read_buf = __archive_read_filter_ahead(self->upstream, - 4 + compressed_size + checksum_size, &bytes_remaining); + 4 + compressed_size + checksum_size, NULL); if (read_buf == NULL) goto truncated_error; @@ -673,7 +709,6 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p) { struct private_data *state = (struct private_data *)self->data; const char *read_buf; - ssize_t bytes_remaining; ssize_t ret; if (state->stage == SELECT_STREAM) { @@ -697,7 +732,7 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p) unsigned int checksum; unsigned int checksum_stream; read_buf = __archive_read_filter_ahead(self->upstream, - 4, &bytes_remaining); + 4, NULL); if (read_buf == NULL) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "truncated lz4 input"); diff --git a/libarchive-clib/c/archive_read_support_filter_lzop.c b/libarchive-clib/c/archive_read_support_filter_lzop.c index 12ed78c..40c4575 100644 --- a/libarchive-clib/c/archive_read_support_filter_lzop.c +++ b/libarchive-clib/c/archive_read_support_filter_lzop.c @@ -71,7 +71,6 @@ struct read_lzop { unsigned char *out_block; size_t out_block_size; - int64_t total_out; int flags; uint32_t compressed_cksum; uint32_t uncompressed_cksum; @@ -118,7 +117,6 @@ archive_read_support_filter_lzop(struct archive *_a) #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) return (ARCHIVE_OK); #else - /* Return ARCHIVE_WARN since this always uses an external program. */ archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external lzop program for lzop decompression"); return (ARCHIVE_WARN); @@ -133,12 +131,11 @@ lzop_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *p; - ssize_t avail; (void)self; /* UNUSED */ - p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, NULL); + if (p == NULL) return (0); if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN)) @@ -429,12 +426,11 @@ lzop_filter_read(struct archive_read_filter *self, const void **p) } /* - * If the both uncompressed size and compressed size are the same, + * If both uncompressed size and compressed size are the same, * we do not decompress this block. */ if (state->uncompressed_size == state->compressed_size) { *p = b; - state->total_out += state->compressed_size; state->unconsumed_bytes = state->compressed_size; return ((ssize_t)state->uncompressed_size); } @@ -478,7 +474,6 @@ lzop_filter_read(struct archive_read_filter *self, const void **p) __archive_read_filter_consume(self->upstream, state->compressed_size); *p = state->out_block; - state->total_out += out_size; return ((ssize_t)out_size); } diff --git a/libarchive-clib/c/archive_read_support_filter_rpm.c b/libarchive-clib/c/archive_read_support_filter_rpm.c index 25ace4a..1213df6 100644 --- a/libarchive-clib/c/archive_read_support_filter_rpm.c +++ b/libarchive-clib/c/archive_read_support_filter_rpm.c @@ -38,20 +38,7 @@ #include "archive_read_private.h" struct rpm { - int64_t total_in; - uint64_t hpos; - uint64_t hlen; - unsigned char header[16]; - enum { - ST_LEAD, /* Skipping 'Lead' section. */ - ST_HEADER, /* Reading 'Header' section; - * first 16 bytes. */ - ST_HEADER_DATA, /* Skipping 'Header' section. */ - ST_PADDING, /* Skipping padding data after the - * 'Header' section. */ - ST_ARCHIVE /* Reading 'Archive' section. */ - } state; - int first_header; + int data_reached; }; #define RPM_LEAD_SIZE 96 /* Size of 'Lead' section. */ #define RPM_MIN_HEAD_SIZE 16 /* Minimum size of 'Head'. */ @@ -64,8 +51,6 @@ static ssize_t rpm_filter_read(struct archive_read_filter *, const void **); static int rpm_filter_close(struct archive_read_filter *); -static inline size_t rpm_limit_bytes(uint64_t, size_t); - #if ARCHIVE_VERSION_NUMBER < 4000000 /* Deprecated; remove in libarchive 4.0 */ int @@ -95,12 +80,11 @@ rpm_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *b; - ssize_t avail; int bits_checked; (void)self; /* UNUSED */ - b = __archive_read_filter_ahead(filter, 8, &avail); + b = __archive_read_filter_ahead(filter, 8, NULL); if (b == NULL) return (0); @@ -152,133 +136,107 @@ rpm_bidder_init(struct archive_read_filter *self) } self->data = rpm; - rpm->state = ST_LEAD; + rpm->data_reached = 0; self->vtable = &rpm_reader_vtable; return (ARCHIVE_OK); } -static inline size_t -rpm_limit_bytes(uint64_t bytes, size_t max) +static ssize_t +skip_padding(struct archive_read_filter *self) { - return (bytes > max ? max : (size_t)bytes); + const unsigned char *h; + ssize_t avail, count, r; + + do { + h = __archive_read_filter_ahead(self->upstream, 1, &avail); + if (h == NULL) + return (ARCHIVE_FATAL); + for (count = 0; count < avail && *h++ == '\0'; count++) + ; + r = __archive_read_filter_consume(self->upstream, count); + if (r < 0) + return (r); + } while (count == avail); + + return (ARCHIVE_OK); } static ssize_t -rpm_filter_read(struct archive_read_filter *self, const void **buff) +skip_prologue(struct archive_read_filter *self) { - struct rpm *rpm; - const unsigned char *b; - ssize_t avail_in, total, used; - size_t n; - uint64_t section; - uint64_t bytes; + const unsigned char *h; + ssize_t r; + int header, seen_header = 0; + + /* Skip lead size. */ + r = __archive_read_filter_consume(self->upstream, RPM_LEAD_SIZE); + if (r < 0) + return (r); - rpm = (struct rpm *)self->data; - *buff = NULL; - total = avail_in = 0; - b = NULL; - used = 0; do { - if (b == NULL) { - b = __archive_read_filter_ahead(self->upstream, 1, - &avail_in); - if (b == NULL) { - if (avail_in < 0) - return (ARCHIVE_FATAL); - else - break; - } + /* Read header intro. */ + h = __archive_read_filter_ahead(self->upstream, + RPM_MIN_HEAD_SIZE, NULL); + if (h == NULL) + return (ARCHIVE_FATAL); + + header = (memcmp(h, "\x8E\xAD\xE8\x01", 4) == 0); + if (header) { + int64_t bytes, length, section; + + seen_header = 1; + + /* Calculate header length. */ + section = archive_be32dec(h + 8); + bytes = archive_be32dec(h + 12); + length = RPM_MIN_HEAD_SIZE + section * 16 + bytes; + + /* Skip header. */ + r = __archive_read_filter_consume(self->upstream, + length); + if (r < 0) + return (r); + + /* Skip padding. */ + r = skip_padding(self); + if (r != ARCHIVE_OK) + return (r); } + } while (header); + + /* At least one header must have been encountered. */ + if (!seen_header) { + archive_set_error( + &self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unrecognized rpm header"); + return (ARCHIVE_FATAL); + } - switch (rpm->state) { - case ST_LEAD: - if (rpm->total_in + avail_in < RPM_LEAD_SIZE) - used += avail_in; - else { - n = (size_t)(RPM_LEAD_SIZE - rpm->total_in); - used += n; - b += n; - rpm->state = ST_HEADER; - rpm->hpos = 0; - rpm->hlen = 0; - rpm->first_header = 1; - } - break; - case ST_HEADER: - n = rpm_limit_bytes(RPM_MIN_HEAD_SIZE - rpm->hpos, - avail_in - used); - memcpy(rpm->header+rpm->hpos, b, n); - b += n; - used += n; - rpm->hpos += n; - - if (rpm->hpos == RPM_MIN_HEAD_SIZE) { - if (rpm->header[0] != 0x8e || - rpm->header[1] != 0xad || - rpm->header[2] != 0xe8 || - rpm->header[3] != 0x01) { - if (rpm->first_header) { - archive_set_error( - &self->archive->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Unrecognized rpm header"); - return (ARCHIVE_FATAL); - } - rpm->state = ST_ARCHIVE; - *buff = rpm->header; - total = RPM_MIN_HEAD_SIZE; - break; - } - /* Calculate 'Header' length. */ - section = archive_be32dec(rpm->header+8); - bytes = archive_be32dec(rpm->header+12); - rpm->hlen = rpm->hpos + section * 16 + bytes; - rpm->state = ST_HEADER_DATA; - rpm->first_header = 0; - } - break; - case ST_HEADER_DATA: - n = rpm_limit_bytes(rpm->hlen - rpm->hpos, - avail_in - used); - b += n; - used += n; - rpm->hpos += n; - if (rpm->hpos == rpm->hlen) - rpm->state = ST_PADDING; - break; - case ST_PADDING: - while (used < avail_in) { - if (*b != 0) { - /* Read next header. */ - rpm->state = ST_HEADER; - rpm->hpos = 0; - rpm->hlen = 0; - break; - } - b++; - used++; - } - break; - case ST_ARCHIVE: - *buff = b; - total = avail_in; - used = avail_in; - break; - } - if (used == avail_in) { - rpm->total_in += used; - __archive_read_filter_consume(self->upstream, used); - b = NULL; - used = 0; - } - } while (total == 0 && avail_in > 0); + return (ARCHIVE_OK); +} - if (used > 0 && b != NULL) { - rpm->total_in += used; - __archive_read_filter_consume(self->upstream, used); +static ssize_t +rpm_filter_read(struct archive_read_filter *self, const void **buff) +{ + struct rpm *rpm; + ssize_t r; + + rpm = (struct rpm *)self->data; + + if (!rpm->data_reached) { + r = skip_prologue(self); + if (r != ARCHIVE_OK) + return (r); + rpm->data_reached = 1; } - return (total); + + *buff = __archive_read_filter_ahead(self->upstream, 1, &r); + if (r > 0) + __archive_read_filter_consume(self->upstream, r); + + return r; } static int diff --git a/libarchive-clib/c/archive_read_support_filter_uu.c b/libarchive-clib/c/archive_read_support_filter_uu.c index acb8feb..714c7e8 100644 --- a/libarchive-clib/c/archive_read_support_filter_uu.c +++ b/libarchive-clib/c/archive_read_support_filter_uu.c @@ -43,8 +43,6 @@ /* Maximum lookahead during bid phase */ #define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */ -#define UUENCODE_MAX_LINE_LENGTH 34*1024 /* in bytes */ - struct uudecode { int64_t total; unsigned char *in_buff; @@ -224,7 +222,7 @@ bid_get_line(struct archive_read_filter *filter, len = get_line(*b, *avail, nl); /* - * Read bytes more while it does not reach the end of line. + * Read more bytes while it does not reach the end of line. */ while (*nl == 0 && len == *avail && !quit && *nbytes_read < UUENCODE_BID_MAX_READ) { @@ -423,7 +421,6 @@ ensure_in_buff_size(struct archive_read_filter *self, /* Allocate the new buffer. */ ptr = malloc(newsize); if (ptr == NULL) { - free(ptr); archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for uudecode"); @@ -480,13 +477,15 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) used = 0; total = 0; out = uudecode->out_buff; + if (avail_in > 2 * UUENCODE_BID_MAX_READ) + avail_in = 2 * UUENCODE_BID_MAX_READ; ravail = avail_in; if (uudecode->state == ST_IGNORE) { used = avail_in; goto finish; } if (uudecode->in_cnt) { - if (uudecode->in_cnt > UUENCODE_MAX_LINE_LENGTH) { + if (uudecode->in_cnt > UUENCODE_BID_MAX_READ) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid format data"); @@ -523,6 +522,12 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) "Insufficient compressed data"); return (ARCHIVE_FATAL); } + if (len > UUENCODE_BID_MAX_READ) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid format data"); + return (ARCHIVE_FATAL); + } llen = len; if ((nl == 0) && (uudecode->state != ST_UUEND)) { if (total == 0 && ravail <= 0) { @@ -544,7 +549,7 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) uudecode->in_cnt = len; if (total == 0) { /* Do not return 0; it means end-of-file. - * We should try to read bytes more. */ + * We should try to read more bytes. */ __archive_read_filter_consume( self->upstream, ravail); goto read_more; @@ -586,11 +591,11 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) if (uudecode->name != NULL) free(uudecode->name); uudecode->name = malloc(namelen + 1); - if (uudecode->name == NULL) { - archive_set_error( - &self->archive->archive, - ENOMEM, - "Can't allocate data for uudecode"); + if (uudecode->name == NULL) { + archive_set_error( + &self->archive->archive, + ENOMEM, + "Can't allocate data for uudecode"); return (ARCHIVE_FATAL); } strncpy(uudecode->name, diff --git a/libarchive-clib/c/archive_read_support_filter_xz.c b/libarchive-clib/c/archive_read_support_filter_xz.c index dcbf734..22cbb26 100644 --- a/libarchive-clib/c/archive_read_support_filter_xz.c +++ b/libarchive-clib/c/archive_read_support_filter_xz.c @@ -54,7 +54,6 @@ struct private_data { lzma_stream stream; unsigned char *out_block; size_t out_block_size; - int64_t total_out; char eof; /* True = found end of compressed data. */ char in_stream; @@ -83,8 +82,7 @@ static int xz_lzma_bidder_init(struct archive_read_filter *); /* * Note that we can detect xz and lzma compressed files even if we * can't decompress them. (In fact, we like detecting them because we - * can give better error messages.) So the bid framework here gets - * compiled even if no lzma library is available. + * can give better error messages.) */ static int xz_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -203,11 +201,10 @@ xz_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; - ssize_t avail; (void)self; /* UNUSED */ - buffer = __archive_read_filter_ahead(filter, 6, &avail); + buffer = __archive_read_filter_ahead(filter, 6, NULL); if (buffer == NULL) return (0); @@ -237,14 +234,13 @@ lzma_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; - ssize_t avail; uint32_t dicsize; uint64_t uncompressed_size; int bits_checked; (void)self; /* UNUSED */ - buffer = __archive_read_filter_ahead(filter, 14, &avail); + buffer = __archive_read_filter_ahead(filter, 14, NULL); if (buffer == NULL) return (0); @@ -343,11 +339,10 @@ static int lzip_has_member(struct archive_read_filter *filter) { const unsigned char *buffer; - ssize_t avail; int bits_checked; int log2dic; - buffer = __archive_read_filter_ahead(filter, 6, &avail); + buffer = __archive_read_filter_ahead(filter, 6, NULL); if (buffer == NULL) return (0); @@ -526,6 +521,7 @@ xz_lzma_bidder_init(struct archive_read_filter *self) free(state->out_block); free(state); self->data = NULL; + self->vtable = NULL; return (ARCHIVE_FATAL); } @@ -536,12 +532,11 @@ lzip_init(struct archive_read_filter *self) const unsigned char *h; lzma_filter filters[2]; unsigned char props[5]; - ssize_t avail_in; uint32_t dicsize; int log2dic, ret; state = (struct private_data *)self->data; - h = __archive_read_filter_ahead(self->upstream, 6, &avail_in); + h = __archive_read_filter_ahead(self->upstream, 6, NULL); if (h == NULL) return (ARCHIVE_FATAL); @@ -706,7 +701,6 @@ xz_filter_read(struct archive_read_filter *self, const void **p) } decompressed = state->stream.next_out - state->out_block; - state->total_out += decompressed; state->member_out += decompressed; if (decompressed == 0) { if (member_in != state->member_in && diff --git a/libarchive-clib/c/archive_read_support_filter_zstd.c b/libarchive-clib/c/archive_read_support_filter_zstd.c index da7c540..100c2c1 100644 --- a/libarchive-clib/c/archive_read_support_filter_zstd.c +++ b/libarchive-clib/c/archive_read_support_filter_zstd.c @@ -48,6 +48,7 @@ #include "archive.h" #include "archive_endian.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -57,7 +58,6 @@ struct private_data { ZSTD_DStream *dstream; unsigned char *out_block; size_t out_block_size; - int64_t total_out; char in_frame; /* True = in the middle of a zstd frame. */ char eof; /* True = found end of compressed data. */ }; @@ -70,8 +70,7 @@ static int zstd_filter_close(struct archive_read_filter *); /* * Note that we can detect zstd compressed files even if we can't decompress * them. (In fact, we like detecting them because we can give better error - * messages.) So the bid framework here gets compiled even if no zstd library - * is available. + * messages.) */ static int zstd_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -110,55 +109,77 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self, { const unsigned char *buffer; ssize_t avail; - - // Zstandard skippable frames contain a 4 byte magic number followed by - // a 4 byte frame data size, then that number of bytes of data. Regular - // frames contain a 4 byte magic number followed by a 2-14 byte frame - // header, some data, and a 3 byte end marker. - ssize_t min_zstd_frame_size = 8; - - ssize_t offset_in_buffer = 0; - ssize_t max_lookahead = 64 * 1024; - - // Zstd regular frame magic number. - uint32_t zstd_magic = 0xFD2FB528U; - - // Note: Zstd and LZ4 skippable frame magic numbers are identical. - // To differentiate these two, we need to look for a non-skippable - // frame. - uint32_t zstd_magic_skippable_start = 0x184D2A50; - uint32_t zstd_magic_skippable_mask = 0xFFFFFFF0; - - (void) self; // UNUSED - - buffer = __archive_read_filter_ahead(filter, min_zstd_frame_size, &avail); + /* + * Zstandard skippable frames contain a 4 byte magic number followed + * by a 4 byte frame data size, then that number of bytes of data. + * Regular frames contain a 4 byte magic number followed by a 2-14 + * byte frame header, some data, and a 3 byte end marker. + */ + const size_t min_zstd_frame_size = 8; + + size_t offset_in_buffer = 0; + const size_t max_lookahead = 64 * 1024; + uint32_t magic_number; + + /* Zstd regular frame magic number. */ + const uint32_t zstd_magic = 0xFD2FB528U; + + /* + * Note: Zstd and LZ4 skippable frame magic numbers are identical. + * To differentiate these two, we need to look for a non-skippable + * frame. + */ + const uint32_t zstd_magic_skippable_start = 0x184D2A50; + const uint32_t zstd_magic_skippable_mask = 0xFFFFFFF0; + + (void) self; /* UNUSED */ + + buffer = __archive_read_filter_ahead(filter, min_zstd_frame_size, + &avail); if (buffer == NULL) return (0); - uint32_t magic_number = archive_le32dec(buffer); + magic_number = archive_le32dec(buffer); - while ((magic_number & zstd_magic_skippable_mask) == zstd_magic_skippable_start) { + while ((magic_number & zstd_magic_skippable_mask) == + zstd_magic_skippable_start) { + size_t min; + uint32_t frame_data_size; - offset_in_buffer += 4; // Skip over the magic number + /* Skip over the magic number */ + offset_in_buffer += 4; - // Ensure that we can read another 4 bytes. - if (offset_in_buffer + 4 > avail) { - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + 4, &avail); + /* Ensure that we can read another 4 bytes. */ + if (offset_in_buffer + 4 > (size_t)avail) { + buffer = __archive_read_filter_ahead(filter, + offset_in_buffer + 4, &avail); if (buffer == NULL) return (0); } - uint32_t frame_data_size = archive_le32dec(buffer + offset_in_buffer); - - // Skip over the 4 frame data size bytes, plus the value stored there. - offset_in_buffer += 4 + frame_data_size; - - // There should be at least one more frame if this is zstd data. - if (offset_in_buffer + min_zstd_frame_size > avail) { - if (offset_in_buffer + min_zstd_frame_size > max_lookahead) + frame_data_size = archive_le32dec(buffer + offset_in_buffer); + + /* Skip over the 4 frame data size bytes */ + offset_in_buffer += 4; + + /* Skip over the value stored there. */ + if (archive_ckd_add_size(&offset_in_buffer, + offset_in_buffer, frame_data_size)) + return (0); + + /* + * There should be at least one more frame + * if this is zstd data. + */ + if (archive_ckd_add_size(&min, + offset_in_buffer, min_zstd_frame_size)) + return (0); + if (min > (size_t)avail) { + if (min > max_lookahead) return (0); - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + min_zstd_frame_size, &avail); + buffer = __archive_read_filter_ahead(filter, + min, &avail); if (buffer == NULL) return (0); } @@ -166,8 +187,10 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self, magic_number = archive_le32dec(buffer + offset_in_buffer); } - // We have skipped over any skippable frames. Either a regular zstd frame - // follows, or this isn't zstd data. + /* + * We have skipped over any skippable frames. Either a regular zstd + * frame follows, or this isn't zstd data. + */ if (magic_number == zstd_magic) return (offset_in_buffer + 4); @@ -179,7 +202,7 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self, /* * If we don't have the library on this system, we can't do the - * decompression directly. We can, however, try to run "zstd -d" + * decompression directly. We can, however, try to run "zstd -d -qq" * in case that's available. */ static int @@ -310,7 +333,6 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) } decompressed = out.pos; - state->total_out += decompressed; if (decompressed == 0) *p = NULL; else diff --git a/libarchive-clib/c/archive_read_support_format_7zip.c b/libarchive-clib/c/archive_read_support_format_7zip.c index 8926ac5..0b476ed 100644 --- a/libarchive-clib/c/archive_read_support_format_7zip.c +++ b/libarchive-clib/c/archive_read_support_format_7zip.c @@ -67,6 +67,7 @@ #define SFX_MIN_ADDR 0x27000 #define SFX_MAX_ADDR 0x60000 #define SFX_MAX_OFFSET (SFX_MAX_ADDR - SFX_MIN_ADDR) +#define SFX_MAX_SEEK 0x800000 /* * PE format @@ -174,7 +175,7 @@ struct _7z_digests { struct _7z_folder { uint64_t numCoders; struct _7z_coder { - unsigned long codec; + uint64_t codec; uint64_t numInStreams; uint64_t numOutStreams; uint64_t propertiesSize; @@ -334,7 +335,7 @@ struct _7zip { int stream_valid; #endif /* Decoding Zstandard data. */ -#if HAVE_ZSTD_H +#if HAVE_ZSTD_H && HAVE_LIBZSTD ZSTD_DStream *zstd_dstream; int zstdstream_valid; #endif @@ -345,12 +346,10 @@ struct _7zip { IByteIn bytein; struct { const unsigned char *next_in; - int64_t avail_in; - int64_t total_in; - int64_t stream_in; + size_t avail_in; + size_t stream_in; unsigned char *next_out; - int64_t avail_out; - int64_t total_out; + size_t avail_out; int overconsumed; } ppstream; int ppmd7_valid; @@ -398,6 +397,27 @@ struct _7zip { * the files. */ #define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000) +/* + * Files without unpack streams must be described by the EmptyStream bitmap, + * which consumes one bit for every file entry in FilesInfo. + */ +static int +files_info_numfiles_is_sane(const struct _7zip *zip) +{ + uint64_t empty_stream_map_bytes; + + if (zip->numFiles > UMAX_ENTRY) + return (0); + if (zip->numFiles > SIZE_MAX / sizeof(*zip->entries)) + return (0); + + if (zip->numFiles <= zip->si.ss.unpack_streams) + return (1); + + empty_stream_map_bytes = (zip->numFiles + 7) / 8; + return (empty_stream_map_bytes <= zip->header_bytes_remaining); +} + static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *); static int archive_read_support_format_7zip_capabilities(struct archive_read *a); static int archive_read_format_7zip_bid(struct archive_read *, int); @@ -407,8 +427,8 @@ static int archive_read_format_7zip_read_data(struct archive_read *, static int archive_read_format_7zip_read_data_skip(struct archive_read *); static int archive_read_format_7zip_read_header(struct archive_read *, struct archive_entry *); -static int check_7zip_header_in_sfx(const char *); -static unsigned long decode_codec_id(const unsigned char *, size_t); +static int check_7zip_header_in_sfx(const unsigned char *); +static int decode_codec_id(const unsigned char *, size_t, uint64_t *); static int decode_encoded_header_info(struct archive_read *, struct _7z_stream_info *); static int decompress(struct archive_read *, struct _7zip *, @@ -449,9 +469,9 @@ static ssize_t read_stream(struct archive_read *, const void **, size_t, size_t); static int seek_pack(struct archive_read *); static int64_t skip_stream(struct archive_read *, size_t); -static int skip_sfx(struct archive_read *, const ssize_t); -static ssize_t find_pe_overlay(struct archive_read *); -static ssize_t find_elf_data_sec(struct archive_read *); +static int get_data_offset(struct archive_read *, int64_t *, int); +static int get_pe_sfx_offset(struct archive_read *, int64_t *); +static int get_elf_sfx_offset(struct archive_read *, int64_t *, int); static int slurp_central_directory(struct archive_read *, struct _7zip *, struct _7z_header_info *); static int setup_decode_folder(struct archive_read *, struct _7z_folder *, @@ -464,6 +484,7 @@ static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t); static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); static size_t sparc_Convert(struct _7zip *, uint8_t *, size_t); static size_t powerpc_Convert(struct _7zip *, uint8_t *, size_t); +static int64_t seek_compat(struct archive_read *, int64_t, int, int); int @@ -530,64 +551,92 @@ archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a) } static int -archive_read_format_7zip_bid(struct archive_read *a, int best_bid) +get_data_offset(struct archive_read *a, int64_t *data_offset, int compat) { - const char *p; - - /* If someone has already bid more than 32, then avoid - trashing the look-ahead buffers with a seek. */ - if (best_bid > 32) - return (-1); + const unsigned char *p; + int64_t offset, sfx_offset; + int r, window; - if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) - return (0); + if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated 7-Zip file body"); + return (ARCHIVE_FATAL); + } /* If first six bytes are the 7-Zip signature, - * return the bid right now. */ - if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) - return (48); + * return the offset right now. */ + if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) { + *data_offset = 0; + return (ARCHIVE_OK); + } /* - * It may a 7-Zip SFX archive file. If first two bytes are - * 'M' and 'Z' available on Windows or first four bytes are - * "\x7F\x45LF" available on posix like system, seek the 7-Zip - * signature. While find_pe_overlay can be performed without - * performing a seek, find_elf_data_sec requires one, + * It may be a 7-Zip SFX archive file. If first two bytes are + * 'M' and 'Z' (PE, Windows) or first four bytes are + * "\x7F\x45LF" (ELF, Posix-like systems), seek the 7-Zip + * signature. While get_pe_sfx_offset can be performed without + * performing a seek, get_elf_sfx_offset requires one, * thus a performance difference between the two is expected. */ - if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { - const ssize_t min_addr = p[0] == 'M' ? find_pe_overlay(a) : - find_elf_data_sec(a); - ssize_t offset = min_addr; - ssize_t window = 4096; + if ((p[0] == 'M' && p[1] == 'Z')) + r = get_pe_sfx_offset(a, &sfx_offset); + else if (memcmp(p, "\x7F\x45LF", 4) == 0) + r = get_elf_sfx_offset(a, &sfx_offset, compat); + else + r = ARCHIVE_FATAL; + if (r < ARCHIVE_WARN || sfx_offset > SFX_MAX_SEEK) + goto fail; + + offset = sfx_offset; + window = 4096; + while (offset + window <= (sfx_offset + SFX_MAX_OFFSET)) { ssize_t bytes_avail; - while (offset + window <= (min_addr + SFX_MAX_OFFSET)) { - const char *buff = __archive_read_ahead(a, - offset + window, &bytes_avail); - if (buff == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 0x40) - return (0); - continue; - } - p = buff + offset; - while (p + 32 < buff + bytes_avail) { - int step = check_7zip_header_in_sfx(p); - if (step == 0) - return (48); - p += step; + const unsigned char *buff = __archive_read_ahead(a, + offset + window, &bytes_avail); + if (buff == NULL) { + /* Remaining bytes are less than window. */ + window >>= 1; + if (window < 0x40) + goto fail; + continue; + } + p = buff + offset; + while (p + 32 < buff + bytes_avail) { + int step = check_7zip_header_in_sfx(p); + if (step == 0) { + *data_offset = p - buff; + return (ARCHIVE_OK); } - offset = p - buff; + p += step; } + offset = p - buff; } - return (0); +fail: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Couldn't find out 7-Zip header"); + return (ARCHIVE_FATAL); +} + +static int +archive_read_format_7zip_bid(struct archive_read *a, int best_bid) +{ + int64_t data_offset; + + /* If someone has already bid more than 32, then avoid + trashing the look-ahead buffers with a seek. */ + if (best_bid > 32) + return (-1); + + if (get_data_offset(a, &data_offset, 0) < 0) + return (0); + + return (48); } static int -check_7zip_header_in_sfx(const char *p) +check_7zip_header_in_sfx(const unsigned char *p) { - switch ((unsigned char)p[5]) { + switch (p[5]) { case 0x1C: if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) return (6); @@ -596,8 +645,7 @@ check_7zip_header_in_sfx(const char *p) * Magic Code, so we should do this in order not to * make a mis-detection. */ - if (crc32(0, (const unsigned char *)p + 12, 20) - != archive_le32dec(p + 8)) + if (crc32(0, p + 12, 20) != archive_le32dec(p + 8)) return (6); /* Hit the header! */ return (0); @@ -611,79 +659,33 @@ check_7zip_header_in_sfx(const char *p) } static int -skip_sfx(struct archive_read *a, const ssize_t min_addr) -{ - const void *h; - const char *p, *q; - size_t skip, offset; - ssize_t bytes, window; - - if (__archive_read_seek(a, min_addr, SEEK_SET) < 0) - return (ARCHIVE_FATAL); - - offset = 0; - window = 1; - while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) { - h = __archive_read_ahead(a, window, &bytes); - if (h == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 0x40) - goto fatal; - continue; - } - if (bytes < 6) { - /* This case might happen when window == 1. */ - window = 4096; - continue; - } - p = (const char *)h; - q = p + bytes; - - /* - * Scan ahead until we find something that looks - * like the 7-Zip header. - */ - while (p + 32 < q) { - int step = check_7zip_header_in_sfx(p); - if (step == 0) { - struct _7zip *zip = - (struct _7zip *)a->format->data; - skip = p - (const char *)h; - __archive_read_consume(a, skip); - zip->seek_base = min_addr + offset + skip; - return (ARCHIVE_OK); - } - p += step; - } - skip = p - (const char *)h; - __archive_read_consume(a, skip); - offset += skip; - if (window == 1) - window = 4096; - } -fatal: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Couldn't find out 7-Zip header"); - return (ARCHIVE_FATAL); -} - -static ssize_t -find_pe_overlay(struct archive_read *a) +get_pe_sfx_offset(struct archive_read *a, int64_t *sfx_offset) { const char *h; - ssize_t bytes, max_offset, offset, sec_end; - ssize_t opt_hdr_sz, sec_cnt; + int64_t max_offset, offset; + ssize_t bytes; + uint16_t opt_hdr_sz, sec_cnt; + + /* + * If encounter any weirdness, revert to old brute-force style search + */ + *sfx_offset = SFX_MIN_ADDR; for (;;) { /* * Read Dos header to find e_lfanew */ h = __archive_read_ahead(a, PE_DOS_HDR_LEN, &bytes); - if (h == NULL || h[0] != 'M' || h[1] != 'Z') { + if (h == NULL) { + return (ARCHIVE_FATAL); + } + if (h[0] != 'M' || h[1] != 'Z') { break; } offset = archive_le32dec(h + PE_DOS_HDR_ELFANEW_OFFSET); + if (offset > SFX_MAX_SEEK) { + return (ARCHIVE_FATAL); + } /* * Read COFF header to find opt header size and sec cnt @@ -691,8 +693,10 @@ find_pe_overlay(struct archive_read *a) if (bytes < offset + PE_COFF_HDR_LEN) { h = __archive_read_ahead(a, offset + PE_COFF_HDR_LEN, &bytes); - if (h == NULL || h[offset] != 'P' || - h[offset + 1] != 'E') { + if (h == NULL) { + return (ARCHIVE_FATAL); + } + if (h[offset] != 'P' || h[offset + 1] != 'E') { break; } } @@ -710,6 +714,10 @@ find_pe_overlay(struct archive_read *a) break; } + if (offset + sec_cnt * PE_SEC_HDR_LEN > SFX_MAX_SEEK) { + return (ARCHIVE_FATAL); + } + /* * Traverse sec table to find max raw offset (i.e., overlay) */ @@ -717,12 +725,14 @@ find_pe_overlay(struct archive_read *a) h = __archive_read_ahead(a, offset + sec_cnt * PE_SEC_HDR_LEN, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } } max_offset = offset; while (sec_cnt > 0) { - sec_end = archive_le32dec( + int64_t sec_end; + + sec_end = (int64_t)archive_le32dec( h + offset + PE_SEC_HDR_RAW_SZ_OFFSET) + archive_le32dec( h + offset + PE_SEC_HDR_RAW_ADDR_OFFSET); @@ -732,34 +742,40 @@ find_pe_overlay(struct archive_read *a) offset += PE_SEC_HDR_LEN; sec_cnt--; } - return (max_offset); + *sfx_offset = max_offset; + break; } - /* - * If encounter any weirdness, revert to old brute-force style search - */ - return (SFX_MIN_ADDR); + return (ARCHIVE_OK); } -static ssize_t -find_elf_data_sec(struct archive_read *a) +static int +get_elf_sfx_offset(struct archive_read *a, int64_t *sfx_offset, int compat) { + int64_t r; const char *h; char big_endian, format_64; - ssize_t bytes, min_addr = SFX_MIN_ADDR; - ssize_t request; + size_t request; uint64_t e_shoff, strtab_offset, strtab_size; uint16_t e_shentsize, e_shnum, e_shstrndx; uint16_t (*dec16)(const void *); uint32_t (*dec32)(const void *); uint64_t (*dec64)(const void *); + /* + * If encounter any weirdness, revert to old brute-force style search + */ + *sfx_offset = SFX_MIN_ADDR; + for (;;) { /* * Read Elf header to find bitness & endianness */ - h = __archive_read_ahead(a, ELF_HDR_MIN_LEN, &bytes); - if (h == NULL || memcmp(h, "\x7F\x45LF", 4) != 0) { + h = __archive_read_ahead(a, ELF_HDR_MIN_LEN, NULL); + if (h == NULL) { + return (ARCHIVE_FATAL); + } + if (memcmp(h, "\x7F\x45LF", 4) != 0) { break; } format_64 = h[ELF_HDR_EI_CLASS_OFFSET] == 0x2; @@ -794,20 +810,28 @@ find_elf_data_sec(struct archive_read *a) break; } + if ((int64_t)e_shoff < 0) { + return (ARCHIVE_FATAL); + } + /* * Reading the section table to find strtab section */ - if (__archive_read_seek(a, e_shoff, SEEK_SET) < 0) { - break; + if (seek_compat(a, e_shoff, SEEK_SET, compat) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); } if (format_64) { - request = (size_t)e_shnum * (size_t)e_shentsize + 0x28; + request = (size_t)e_shnum * e_shentsize + 0x28; } else { - request = (size_t)e_shnum * (size_t)e_shentsize + 0x18; + request = (size_t)e_shnum * e_shentsize + 0x18; } - h = __archive_read_ahead(a, request, &bytes); + if (request > SFX_MAX_SEEK) { + return (ARCHIVE_FATAL); + } + h = __archive_read_ahead(a, request, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } if (format_64) { strtab_offset = (*dec64)( @@ -820,45 +844,50 @@ find_elf_data_sec(struct archive_read *a) strtab_size = (*dec32)( h + e_shstrndx * e_shentsize + 0x14); } - if (strtab_size < 6 || strtab_size > SIZE_MAX) - break; + if ((int64_t)strtab_offset < 0 || strtab_size < 6 || + strtab_size > SFX_MAX_SEEK) + return (ARCHIVE_FATAL); /* * Read the STRTAB section to find the .data offset */ - if (__archive_read_seek(a, strtab_offset, SEEK_SET) < 0) { - break; + if (seek_compat(a, strtab_offset, SEEK_SET, compat) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); } h = __archive_read_ahead(a, strtab_size, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } - ssize_t data_sym_offset = -1; + size_t data_sym_offset = strtab_size; for (size_t offset = 0; offset < strtab_size - 6; offset++) { if (memcmp(h + offset, ".data\00", 6) == 0) { data_sym_offset = offset; break; } } - if (data_sym_offset == -1) { + if (data_sym_offset == strtab_size) { break; } /* * Find the section with the .data name */ - if (__archive_read_seek(a, e_shoff, SEEK_SET) < 0) { - break; + if (seek_compat(a, e_shoff, SEEK_SET, compat) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); } - h = __archive_read_ahead(a, (size_t)e_shnum * (size_t)e_shentsize, NULL); + h = __archive_read_ahead(a, (size_t)e_shnum * e_shentsize, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } - ssize_t sec_tbl_offset = 0, name_offset; + size_t sec_tbl_offset = 0; while (e_shnum > 0) { + uint32_t name_offset; + name_offset = (*dec32)(h + sec_tbl_offset); if (name_offset == data_sym_offset) { - uint64_t sel_offset; + int64_t sel_offset; if (format_64) { sel_offset = (*dec64)( @@ -867,9 +896,8 @@ find_elf_data_sec(struct archive_read *a) sel_offset = (*dec32)( h + sec_tbl_offset + 0x10); } - if (sel_offset > SSIZE_MAX) - break; - min_addr = (ssize_t)sel_offset; + if (sel_offset >= 0) + *sfx_offset = sel_offset; break; } sec_tbl_offset += e_shentsize; @@ -878,8 +906,10 @@ find_elf_data_sec(struct archive_read *a) break; } - __archive_read_seek(a, 0, SEEK_SET); - return (min_addr); + r = seek_compat(a, 0, SEEK_SET, compat); + if (r < 0) + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (int)r; } static int @@ -1004,29 +1034,19 @@ archive_read_format_7zip_read_header(struct archive_read *a, const int supported_attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; if (zip_entry->attr & supported_attrs) { - char *fflags_text, *ptr; - /* allocate for ",rdonly,hidden,system" */ - fflags_text = malloc(22 * sizeof(*fflags_text)); - if (fflags_text != NULL) { - ptr = fflags_text; - if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) { - strcpy(ptr, ",rdonly"); - ptr = ptr + 7; - } - if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) { - strcpy(ptr, ",hidden"); - ptr = ptr + 7; - } - if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) { - strcpy(ptr, ",system"); - ptr = ptr + 7; - } - if (ptr > fflags_text) { - archive_entry_copy_fflags_text(entry, - fflags_text + 1); - } - free(fflags_text); - } + char buf[sizeof(",rdonly,hidden,system")]; + char *fflags[3] = { "", "", "" }; + char **flag = fflags; + + if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) + *flag++ = ",rdonly"; + if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) + *flag++ = ",hidden"; + if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) + *flag++ = ",system"; + + snprintf(buf, sizeof(buf), "%s%s%s", fflags[0], fflags[1], fflags[2]); + archive_entry_copy_fflags_text(entry, buf + 1); } /* If there's no body, force read_data() to return EOF immediately. */ @@ -1037,6 +1057,13 @@ archive_read_format_7zip_read_header(struct archive_read *a, unsigned char *symname = NULL; size_t symsize = 0; + if (zip->entry_bytes_remaining > 1024 * 1024) { + archive_set_error(&a->archive, ENOMEM, + "Rejecting malformed 7zip archive: " + "symlink contents exceed 1 megabyte"); + return (ARCHIVE_FATAL); + } + /* * Symbolic-name is recorded as its contents. We have to * read the contents at this time. @@ -1189,7 +1216,7 @@ archive_read_format_7zip_read_data_skip(struct archive_read *a) */ bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining); if (bytes_skipped < 0) - return (ARCHIVE_FATAL); + return ((int)bytes_skipped); zip->entry_bytes_remaining = 0; /* This entry is finished and done. */ @@ -1281,17 +1308,20 @@ set_error(struct archive_read *a, int ret) #endif -static unsigned long -decode_codec_id(const unsigned char *codecId, size_t id_size) +static int +decode_codec_id(const unsigned char *codecId, size_t id_size, uint64_t *id) { unsigned i; - unsigned long id = 0; + uint64_t v = 0; for (i = 0; i < id_size; i++) { - id <<= 8; - id += codecId[i]; + if (v > (uint64_t)INT64_MAX / 256) + return (-1); + v <<= 8; + v += codecId[i]; } - return (id); + *id = v; + return (0); } static Byte @@ -1301,29 +1331,26 @@ ppmd_read(void *p) struct _7zip *zip = (struct _7zip *)(a->format->data); Byte b; - if (zip->ppstream.avail_in <= 0) { + if (zip->ppstream.avail_in == 0) { /* * Ppmd7_DecodeSymbol might require reading multiple bytes * and we are on boundary; * last resort to read using __archive_read_ahead. */ - ssize_t bytes_avail = 0; const uint8_t* data = __archive_read_ahead(a, - (size_t)zip->ppstream.stream_in+1, &bytes_avail); - if(data == NULL || bytes_avail < zip->ppstream.stream_in+1) { + zip->ppstream.stream_in + 1, NULL); + if (data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7z file data"); zip->ppstream.overconsumed = 1; return (0); } - zip->ppstream.next_in++; b = data[zip->ppstream.stream_in]; } else { b = *zip->ppstream.next_in++; + zip->ppstream.avail_in--; } - zip->ppstream.avail_in--; - zip->ppstream.total_in++; zip->ppstream.stream_in++; return (b); } @@ -1352,8 +1379,9 @@ init_decompression(struct archive_read *a, struct _7zip *zip, coder2->codec != _7Z_SPARC) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unsupported filter %lx for %lx", - coder2->codec, coder1->codec); + "Unsupported filter %jx for %jx", + (uintmax_t)coder2->codec, + (uintmax_t)coder1->codec); return (ARCHIVE_FAILED); } zip->codec2 = coder2->codec; @@ -1558,7 +1586,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, #endif case _7Z_ZSTD: { -#if defined(HAVE_ZSTD_H) +#if HAVE_ZSTD_H && HAVE_LIBZSTD if (zip->zstdstream_valid) { ZSTD_freeDStream(zip->zstd_dstream); zip->zstdstream_valid = 0; @@ -1632,8 +1660,6 @@ init_decompression(struct archive_read *a, struct _7zip *zip, zip->ppmd7_valid = 1; zip->ppmd7_stat = 0; zip->ppstream.overconsumed = 0; - zip->ppstream.total_in = 0; - zip->ppstream.total_out = 0; break; } case _7Z_X86: @@ -1840,7 +1866,7 @@ decompress(struct archive_read *a, struct _7zip *zip, t_avail_out = zip->stream.avail_out; break; #endif -#ifdef HAVE_ZSTD_H +#if HAVE_ZSTD_H && HAVE_LIBZSTD case _7Z_ZSTD: { ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos @@ -1916,14 +1942,13 @@ decompress(struct archive_read *a, struct _7zip *zip, } *zip->ppstream.next_out++ = (unsigned char)sym; zip->ppstream.avail_out--; - zip->ppstream.total_out++; if (flush_bytes) flush_bytes--; } while (zip->ppstream.avail_out && (zip->ppstream.avail_in || flush_bytes)); - t_avail_in = (size_t)zip->ppstream.avail_in; - t_avail_out = (size_t)zip->ppstream.avail_out; + t_avail_in = zip->ppstream.avail_in; + t_avail_out = zip->ppstream.avail_out; break; } default: @@ -2027,7 +2052,7 @@ free_decompression(struct archive_read *a, struct _7zip *zip) zip->stream_valid = 0; } #endif -#ifdef HAVE_ZSTD_H +#if HAVE_ZSTD_H && HAVE_LIBZSTD if (zip->zstdstream_valid) ZSTD_freeDStream(zip->zstd_dstream); #endif @@ -2279,7 +2304,8 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) if ((p = header_bytes(a, codec_size)) == NULL) return (-1); - f->coders[i].codec = decode_codec_id(p, codec_size); + if (decode_codec_id(p, codec_size, &f->coders[i].codec) < 0) + return (-1); if (simple) { f->coders[i].numInStreams = 1; @@ -2543,9 +2569,8 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, for (i = 0; i < numFolders; i++) { if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0) return (-1); - if (UMAX_ENTRY < f[i].numUnpackStreams) - return (-1); - if (unpack_streams > SIZE_MAX - UMAX_ENTRY) { + if (f[i].numUnpackStreams > + UMAX_ENTRY - unpack_streams) { return (-1); } unpack_streams += (size_t)f[i].numUnpackStreams; @@ -2556,6 +2581,13 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, } else unpack_streams = numFolders; + if (type != kSize) { + for (i = 0; i < numFolders; i++) { + if (f[i].numUnpackStreams > 1) + return (-1); + } + } + ss->unpack_streams = unpack_streams; if (unpack_streams) { ss->unpackSizes = calloc(unpack_streams, @@ -2599,11 +2631,6 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, type = *p; } - for (i = 0; i < unpack_streams; i++) { - ss->digestsDefined[i] = 0; - ss->digests[i] = 0; - } - numDigests = 0; for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams != 1 || !f[i].digest_defined) @@ -2806,7 +2833,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if (parse_7zip_uint64(a, &(zip->numFiles)) < 0) return (-1); - if (UMAX_ENTRY < zip->numFiles) + if (!files_info_numfiles_is_sane(zip)) return (-1); zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries)); @@ -2828,7 +2855,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if (parse_7zip_uint64(a, &size) < 0) return (-1); - if (zip->header_bytes_remaining < size) + if (zip->header_bytes_remaining < size || size > SIZE_MAX / 4) return (-1); ll = (size_t)size; @@ -3235,22 +3262,22 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, uint64_t next_header_size; uint32_t next_header_crc; ssize_t bytes_avail; + int64_t data_offset; int check_header_crc, r; - if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) + if (get_data_offset(a, &data_offset, 1) < 0) + return (ARCHIVE_FATAL); + if (__archive_read_consume(a, data_offset) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); return (ARCHIVE_FATAL); - - if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { - /* This is an executable ? Must be self-extracting... */ - const ssize_t min_addr = p[0] == 'M' ? find_pe_overlay(a) : - find_elf_data_sec(a); - r = skip_sfx(a, min_addr); - if (r < ARCHIVE_WARN) - return (r); - if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) - return (ARCHIVE_FATAL); } - zip->seek_base += 32; + if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated 7-Zip file header"); + return (ARCHIVE_FATAL); + } + + zip->seek_base = (uint64_t)data_offset + 32; if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) { archive_set_error(&a->archive, -1, "Not 7-Zip archive file"); @@ -3258,7 +3285,7 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, } /* CRC check. */ - if (crc32(0, (const unsigned char *)p + 12, 20) + if (crc32(0, p + 12, 20) != archive_le32dec(p + 8)) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, -1, "Header CRC error"); @@ -3282,9 +3309,11 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, if (next_header_offset != 0) { if (bytes_avail >= (ssize_t)next_header_offset) __archive_read_consume(a, next_header_offset); - else if (__archive_read_seek(a, - next_header_offset + zip->seek_base, SEEK_SET) < 0) + else if (seek_compat(a, + next_header_offset + zip->seek_base, SEEK_SET, 1) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); return (ARCHIVE_FATAL); + } } zip->stream_offset = next_header_offset; zip->header_offset = next_header_offset; @@ -3620,9 +3649,11 @@ seek_pack(struct archive_read *a) zip->si.pi.sizes[zip->pack_stream_index]; pack_offset = zip->si.pi.positions[zip->pack_stream_index]; if (zip->stream_offset != pack_offset) { - if (0 > __archive_read_seek(a, pack_offset + zip->seek_base, - SEEK_SET)) + if (0 > seek_compat(a, pack_offset + zip->seek_base, + SEEK_SET, 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); return (ARCHIVE_FATAL); + } zip->stream_offset = pack_offset; } zip->pack_stream_index++; @@ -3688,7 +3719,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, r = setup_decode_folder(a, &(zip->si.ci.folders[zip->folder_index]), 0); if (r != ARCHIVE_OK) - return (ARCHIVE_FATAL); + return (r); zip->folder_index++; } @@ -3763,15 +3794,9 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, } /* - * Initialize a stream reader. - */ - zip->pack_stream_remaining = (unsigned)folder->numPackedStreams; - zip->pack_stream_index = (unsigned)folder->packIndex; - zip->folder_outbytes_remaining = folder_uncompressed_size(folder); - zip->uncompressed_buffer_bytes_remaining = 0; - - /* - * Check coder types. + * Check coder types before modifying any stream-reader state, so that + * an early return leaves zip unchanged (avoids partially-initialized + * state that callers would have to reason about). */ for (i = 0; i < folder->numCoders; i++) { switch(folder->coders[i].codec) { @@ -3789,7 +3814,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, ARCHIVE_ERRNO_MISC, "The %s is encrypted, " "but currently not supported", cname); - return (ARCHIVE_FATAL); + return (header ? ARCHIVE_FATAL : ARCHIVE_FAILED); } case _7Z_X86_BCJ2: { found_bcj2++; @@ -3809,8 +3834,16 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, ARCHIVE_ERRNO_MISC, "The %s is encoded with many filters, " "but currently not supported", cname); - return (ARCHIVE_FATAL); + return (header ? ARCHIVE_FATAL : ARCHIVE_FAILED); } + + /* + * Initialize a stream reader. + */ + zip->pack_stream_remaining = (unsigned)folder->numPackedStreams; + zip->pack_stream_index = (unsigned)folder->packIndex; + zip->folder_outbytes_remaining = folder_uncompressed_size(folder); + zip->uncompressed_buffer_bytes_remaining = 0; coder1 = &(folder->coders[0]); if (folder->numCoders == 2) coder2 = &(folder->coders[1]); @@ -3831,6 +3864,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, ssize_t bytes; unsigned char *b[3] = {NULL, NULL, NULL}; uint64_t sunpack[3] ={-1, -1, -1}; + uint64_t remaining; size_t s[3] = {0, 0, 0}; int idx[3] = {0, 1, 2}; @@ -3885,12 +3919,14 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, coder2 = &(fc[3]); zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[2]; + remaining = folder->unPackSize[2]; } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 && zip->pack_stream_remaining == 4 && folder->numInStreams == 5 && folder->numOutStreams == 2) { /* Source type 0 made by 7z */ zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[0]; + remaining = folder->unPackSize[0]; } else { /* We got an unexpected form. */ archive_set_error(&(a->archive), @@ -3898,6 +3934,15 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, "Unsupported form of BCJ2 streams"); return (ARCHIVE_FATAL); } + if (remaining > SIZE_MAX) { + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_MISC, + "7-Zip sub-stream size exceeds " + "platform maximum"); + return (ARCHIVE_FATAL); + } + zip->main_stream_bytes_remaining = remaining; + /* Skip the main stream at this time. */ if ((r = seek_pack(a)) < 0) @@ -3929,6 +3974,14 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, /* Allocate memory for the decoded data of a sub * stream. */ + if (zip->folder_outbytes_remaining > SIZE_MAX) { + free(b[0]); free(b[1]); free(b[2]); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "7-Zip sub-stream size exceeds " + "platform maximum"); + return (ARCHIVE_FATAL); + } b[i] = malloc((size_t)zip->folder_outbytes_remaining); if (b[i] == NULL) { free(b[0]); free(b[1]); free(b[2]); @@ -4101,9 +4154,7 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) prevPosT = bufferPos; if (Test86MSByte(p[4])) { - uint32_t src = ((uint32_t)p[4] << 24) | - ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) | - ((uint32_t)p[1]); + uint32_t src = archive_le32dec(p + 1); uint32_t dest; for (;;) { uint8_t b; @@ -4163,16 +4214,13 @@ arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size) for (i = 0; i + 4 <= size; i += 4) { if (buf[i + 3] == 0xEB) { // Calculate the transformed addr. - addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) - | ((uint32_t)buf[i + 2] << 16); + addr = archive_le24dec(buf + i); addr <<= 2; addr -= zip->bcj_ip + (uint32_t)i; addr >>= 2; // Store the transformed addr in buf. - buf[i] = (uint8_t)addr; - buf[i + 1] = (uint8_t)(addr >> 8); - buf[i + 2] = (uint8_t)(addr >> 16); + archive_le24enc(buf + i, addr); } } @@ -4203,20 +4251,14 @@ arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) uint32_t addr; for (i = 0; i + 4 <= size; i += 4) { - instr = (uint32_t)buf[i] - | ((uint32_t)buf[i+1] << 8) - | ((uint32_t)buf[i+2] << 16) - | ((uint32_t)buf[i+3] << 24); + instr = archive_le32dec(buf + i); if ((instr >> 26) == 0x25) { /* BL instruction */ addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2); instr = 0x94000000 | (addr & 0x03FFFFFF); - buf[i] = (uint8_t)instr; - buf[i+1] = (uint8_t)(instr >> 8); - buf[i+2] = (uint8_t)(instr >> 16); - buf[i+3] = (uint8_t)(instr >> 24); + archive_le32enc(buf + i, instr); } else if ((instr & 0x9F000000) == 0x90000000) { /* ADRP instruction */ addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC); @@ -4232,10 +4274,7 @@ arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) instr |= (addr & 0x03FFFC) << 3; instr |= (0U - (addr & 0x020000)) & 0xE00000; - buf[i] = (uint8_t)instr; - buf[i+1] = (uint8_t)(instr >> 8); - buf[i+2] = (uint8_t)(instr >> 16); - buf[i+3] = (uint8_t)(instr >> 24); + archive_le32enc(buf + i, instr); } } @@ -4278,10 +4317,7 @@ sparc_Convert(struct _7zip *zip, uint8_t *buf, size_t size) size &= ~(size_t)3; for (i = 0; i < size; i += 4) { - instr = (uint32_t)(buf[i] << 24) - | ((uint32_t)buf[i+1] << 16) - | ((uint32_t)buf[i+2] << 8) - | (uint32_t)buf[i+3]; + instr = archive_be32dec(buf + i); if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) { instr <<= 2; @@ -4290,10 +4326,7 @@ sparc_Convert(struct _7zip *zip, uint8_t *buf, size_t size) instr = ((uint32_t)0x40000000 - (instr & 0x400000)) | 0x40000000 | (instr & 0x3FFFFF); - buf[i] = (uint8_t)(instr >> 24); - buf[i+1] = (uint8_t)(instr >> 16); - buf[i+2] = (uint8_t)(instr >> 8); - buf[i+3] = (uint8_t)instr; + archive_be32enc(buf + i, instr); } } @@ -4504,15 +4537,10 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) buf2 += 4; size2 -= 4; } - dest = (((uint32_t)v[0] << 24) | - ((uint32_t)v[1] << 16) | - ((uint32_t)v[2] << 8) | - ((uint32_t)v[3])) - + dest = archive_be32dec(v) - ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4); - out[0] = (uint8_t)dest; - out[1] = (uint8_t)(dest >> 8); - out[2] = (uint8_t)(dest >> 16); - out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24); + archive_le32enc(out, dest); + zip->bcj2_prevByte = out[3]; for (i = 0; i < 4 && outPos < outSize; i++) outBuf[outPos++] = out[i]; @@ -4538,3 +4566,34 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) return ((ssize_t)outPos); } + +/* + * Perform a seek to given position. If seeking is not supported, + * target position is in front of current position, and compat is requested, + * try to consume bytes until position is reached. + */ +int64_t +seek_compat(struct archive_read *a, int64_t offset, int whence, int compat) +{ + int64_t ret = ARCHIVE_FAILED; + + if (a->filter->can_seek) + ret = __archive_read_seek(a, offset, whence); + else if (compat) { + switch (whence) { + case SEEK_CUR: + ret = __archive_read_consume(a, offset); + break; + case SEEK_SET: + if (a->filter->position > offset) + break; + ret = __archive_read_consume(a, + offset - a->filter->position); + break; + default: + break; + } + } + + return (ret); +} diff --git a/libarchive-clib/c/archive_read_support_format_ar.c b/libarchive-clib/c/archive_read_support_format_ar.c index 6dfe293..5633594 100644 --- a/libarchive-clib/c/archive_read_support_format_ar.c +++ b/libarchive-clib/c/archive_read_support_format_ar.c @@ -45,6 +45,7 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -588,24 +589,22 @@ ar_parse_gnu_filename_table(struct archive_read *a) static uint64_t ar_atol8(const char *p, unsigned char_cnt) { - uint64_t l, limit, last_digit_limit; + uint64_t l; unsigned int digit, base; base = 8; - limit = UINT64_MAX / base; - last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; - while (*p >= '0' && digit < base && char_cnt-- > 0) { - if (l>limit || (l == limit && digit > last_digit_limit)) { + while (*p >= '0' && digit < base && char_cnt-- > 0) { + if (archive_ckd_mul_u64(&l, l, base) || + archive_ckd_add_u64(&l, l, digit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } - l = (l * base) + digit; digit = *++p - '0'; } return (l); @@ -614,23 +613,21 @@ ar_atol8(const char *p, unsigned char_cnt) static uint64_t ar_atol10(const char *p, unsigned char_cnt) { - uint64_t l, limit, last_digit_limit; + uint64_t l; unsigned int base, digit; base = 10; - limit = UINT64_MAX / base; - last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; while (*p >= '0' && digit < base && char_cnt-- > 0) { - if (l > limit || (l == limit && digit > last_digit_limit)) { + if (archive_ckd_mul_u64(&l, l, base) || + archive_ckd_add_u64(&l, l, digit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } - l = (l * base) + digit; digit = *++p - '0'; } return (l); diff --git a/libarchive-clib/c/archive_read_support_format_cab.c b/libarchive-clib/c/archive_read_support_format_cab.c index bf8ac6b..bd82b44 100644 --- a/libarchive-clib/c/archive_read_support_format_cab.c +++ b/libarchive-clib/c/archive_read_support_format_cab.c @@ -112,8 +112,8 @@ struct lzx_dec { unsigned char *bitlen; /* - * Use a index table. It's faster than searching a huffman - * coding tree, which is a binary tree. But a use of a large + * Use an index table. It's faster than searching a huffman + * coding tree, which is a binary tree. But usage of a large * index table causes L1 cache read miss many times. */ int max_bits; @@ -264,7 +264,7 @@ struct cfheader { }; struct cab { - /* entry_bytes_remaining is the number of bytes we expect. */ + /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; @@ -279,7 +279,7 @@ struct cab { struct cfheader cfheader; struct archive_wstring ws; - /* Flag to mark progress that an archive was read their first header.*/ + /* Flag to mark progress that first header of an archive was read.*/ char found_header; char end_of_archive; char end_of_entry; @@ -433,7 +433,7 @@ archive_read_format_cab_bid(struct archive_read *a, int best_bid) /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward - * up to 128k for a 'MSCF' marker. + * up to 128k for an 'MSCF' marker. */ if (p[0] == 'M' && p[1] == 'Z') { offset = 0; @@ -501,7 +501,7 @@ cab_skip_sfx(struct archive_read *a) for (;;) { const char *h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { - /* Remaining size are less than window. */ + /* Remaining size is less than window. */ window >>= 1; if (window < 128) { archive_set_error(&a->archive, @@ -554,19 +554,18 @@ cab_strnlen(const unsigned char *p, size_t maxlen) return ((ssize_t)i); } -/* Read bytes as much as remaining. */ +/* Read up to max remaining bytes. */ static const void * -cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail) +cab_read_ahead_remaining(struct archive_read *a, size_t max, ssize_t *avail) { - const void *p; + const void *p = __archive_read_ahead(a, max, avail); - while (min > 0) { - p = __archive_read_ahead(a, min, avail); - if (p != NULL) - return (p); - min--; - } - return (NULL); + if (p == NULL && *avail > 0) + p = __archive_read_ahead(a, *avail, avail); + if (p != NULL && (size_t)*avail > max) + *avail = max; + + return (p); } /* Convert a path separator '\' -> '/' */ @@ -627,7 +626,7 @@ cab_read_header(struct archive_read *a) struct cab *cab; struct cfheader *hd; size_t bytes, used; - ssize_t len; + ssize_t avail, len; int64_t skip; int err, i; int cur_folder, prev_folder; @@ -691,29 +690,34 @@ cab_read_header(struct archive_read *a) hd->cffolder = 0;/* Avoid compiling warning. */ if (hd->flags & PREV_CABINET) { /* How many bytes are used for szCabinetPrev. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + if ((len = cab_strnlen(p + used, avail - used - 1)) <= 0) { goto invalid; + } used += len + 1; /* How many bytes are used for szDiskPrev. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + if ((len = cab_strnlen(p + used, avail - used - 1)) < 0) goto invalid; used += len + 1; } if (hd->flags & NEXT_CABINET) { /* How many bytes are used for szCabinetNext. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + if ((len = cab_strnlen(p + used, avail - used - 1)) <= 0) goto invalid; used += len + 1; /* How many bytes are used for szDiskNext. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + if ((len = cab_strnlen(p + used, avail - used - 1)) < 0) goto invalid; used += len + 1; } @@ -776,7 +780,7 @@ cab_read_header(struct archive_read *a) */ /* Seek read pointer to the offset of CFFILE if needed. */ skip = (int64_t)hd->files_offset - cab->cab_offset; - if (skip < 0) { + if (skip < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFFILE %jd < %jd", (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset); @@ -795,7 +799,6 @@ cab_read_header(struct archive_read *a) prev_folder = -1; for (i = 0; i < hd->file_count; i++) { struct cffile *file = &(hd->file_array[i]); - ssize_t avail; if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) return (truncated_error(a)); @@ -1336,6 +1339,10 @@ cab_next_cfdata(struct archive_read *a) } return (ARCHIVE_OK); invalid: + cfdata->compressed_size = 0; + cfdata->compressed_bytes_remaining = 0; + cfdata->uncompressed_size = 0; + cfdata->uncompressed_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); @@ -1478,6 +1485,14 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) cab->uncompressed_buffer + cab->stream.total_out; cab->stream.avail_out = cfdata->uncompressed_size - cab->stream.total_out; + if ((size_t)cfdata->uncompressed_size > + cab->uncompressed_buffer_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid CFDATA uncompressed size"); + *avail = ARCHIVE_FATAL; + return (NULL); + } d = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { @@ -1565,11 +1580,9 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) * correctly compute the sum of CFDATA accordingly. */ if (cfdata->compressed_bytes_remaining > 0) { - ssize_t bytes_avail; - d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, - &bytes_avail); - if (bytes_avail <= 0) { + NULL); + if (d == NULL) { *avail = truncated_error(a); return (NULL); } @@ -1737,11 +1750,9 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) * Make sure a read pointer advances to next CFDATA. */ if (cfdata->compressed_bytes_remaining > 0) { - ssize_t bytes_avail; - d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, - &bytes_avail); - if (bytes_avail <= 0) { + NULL); + if (d == NULL) { *avail = truncated_error(a); return (NULL); } @@ -3211,6 +3222,10 @@ lzx_make_huffman_table(struct huffman *hf) bitlen = hf->bitlen; len_avail = hf->len_size; hf->tree_used = 0; + /* Initialize table to invalid values */ + for (i = 0; i < tbl_size; i++) { + tbl[i] = (uint16_t)hf->len_size; + } for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt; diff --git a/libarchive-clib/c/archive_read_support_format_cpio.c b/libarchive-clib/c/archive_read_support_format_cpio.c index 43169f6..86d2bc9 100644 --- a/libarchive-clib/c/archive_read_support_format_cpio.c +++ b/libarchive-clib/c/archive_read_support_format_cpio.c @@ -38,6 +38,7 @@ #endif #include "archive.h" +#include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" @@ -160,6 +161,8 @@ #define afiol_filesize_c_offset 115 /* ':' */ #define afiol_header_size 116 +/* CPIO name fields store a full pathname, including the terminating NUL. */ +#define CPIO_PATHNAME_MAX (1024 * 1024) struct links_entry { struct links_entry *next; @@ -200,7 +203,7 @@ static int archive_read_format_cpio_read_data(struct archive_read *, static int archive_read_format_cpio_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cpio_skip(struct archive_read *); -static int64_t be4(const unsigned char *); +static int64_t be32dec(const unsigned char *); static int find_odc_header(struct archive_read *); static int find_newc_header(struct archive_read *); static int header_bin_be(struct archive_read *, struct cpio *, @@ -215,7 +218,7 @@ static int header_afiol(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); -static int64_t le4(const unsigned char *); +static int64_t le32dec(const unsigned char *); static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry); @@ -303,12 +306,12 @@ archive_read_format_cpio_bid(struct archive_read *a, int best_bid) * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ - } else if (p[0] * 256 + p[1] == 070707) { + } else if (archive_be16dec(p) == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ - } else if (p[0] + p[1] * 256 == 070707) { + } else if (archive_le16dec(p) == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; @@ -385,8 +388,15 @@ archive_read_format_cpio_read_header(struct archive_read *a, if (r < ARCHIVE_WARN) return (r); + if (namelength > CPIO_PATHNAME_MAX) { + archive_set_error(&a->archive, ENOMEM, + "Rejecting malformed cpio archive: " + "pathname exceeds 1 megabyte"); + return (ARCHIVE_FATAL); + } + /* Read name from buffer. */ - h = __archive_read_ahead(a, namelength + name_pad, NULL); + h = __archive_read_ahead(a, namelength, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_pathname_l(entry, @@ -403,7 +413,8 @@ archive_read_format_cpio_read_header(struct archive_read *a, } cpio->entry_offset = 0; - __archive_read_consume(a, namelength + name_pad); + __archive_read_consume(a, namelength); + __archive_read_consume(a, name_pad); /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { @@ -703,13 +714,24 @@ find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; - size_t skip, skipped = 0; + int64_t skip; + uintmax_t skipped = 0; ssize_t bytes; for (;;) { - h = __archive_read_ahead(a, odc_header_size, &bytes); - if (h == NULL) - return (ARCHIVE_FATAL); + size_t header_size; + + header_size = afiol_header_size; + h = __archive_read_ahead(a, afiol_header_size, &bytes); + if (h == NULL) { + if (bytes >= odc_header_size) { + header_size = odc_header_size; + h = __archive_read_ahead(a, odc_header_size, + &bytes); + } + if (h == NULL) + return (ARCHIVE_FATAL); + } p = h; q = p + bytes; @@ -725,7 +747,7 @@ find_odc_header(struct archive_read *a) * Scan ahead until we find something that looks * like an odc header. */ - while (p + odc_header_size <= q) { + while (p + header_size <= q) { switch (p[5]) { case '7': if ((memcmp("070707", p, 6) == 0 @@ -741,9 +763,9 @@ find_odc_header(struct archive_read *a) if (skipped > 0) { archive_set_error(&a->archive, 0, - "Skipped %d bytes before " + "Skipped %ju bytes before " "finding valid header", - (int)skipped); + skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); @@ -828,7 +850,7 @@ header_odc(struct archive_read *a, struct cpio *cpio, * NOTE: if a filename suffix is ".z", it is a file gzipped by afio. * it would be nice if we could show uncompressed file size and * uncompress file contents automatically, unfortunately we have nothing - * to get a uncompressed file size while reading each header. It means + * to get an uncompressed file size while reading each header. It means * we also cannot uncompress file contents under our framework. */ static int @@ -873,7 +895,8 @@ header_afiol(struct archive_read *a, struct cpio *cpio, t = atol16(header + afiol_filesize_offset, afiol_filesize_size); if (t < 0) { - archive_set_error(&a->archive, 0, "Nonsensical file size"); + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Nonsensical file size"); return (ARCHIVE_FATAL); } cpio->entry_bytes_remaining = t; @@ -897,7 +920,7 @@ header_bin_le(struct archive_read *a, struct cpio *cpio, /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } @@ -905,24 +928,24 @@ header_bin_le(struct archive_read *a, struct cpio *cpio, /* Parse out binary fields. */ header = (const unsigned char *)h; - archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256); - archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256); - archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256); + archive_entry_set_dev(entry, archive_le16dec(header + bin_dev_offset)); + archive_entry_set_ino(entry, archive_le16dec(header + bin_ino_offset)); + archive_entry_set_mode(entry, archive_le16dec(header + bin_mode_offset)); if (cpio->option_pwb) { /* turn off random bits left over from V6 inode */ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777); if ((archive_entry_mode(entry) & AE_IFMT) == 0) archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG); } - archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256); - archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256); - archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256); - archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256); - archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0); - *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256; + archive_entry_set_uid(entry, archive_le16dec(header + bin_uid_offset)); + archive_entry_set_gid(entry, archive_le16dec(header + bin_gid_offset)); + archive_entry_set_nlink(entry, archive_le16dec(header + bin_nlink_offset)); + archive_entry_set_rdev(entry, archive_le16dec(header + bin_rdev_offset)); + archive_entry_set_mtime(entry, le32dec(header + bin_mtime_offset), 0); + *namelength = archive_le16dec(header + bin_namesize_offset); *name_pad = *namelength & 1; /* Pad to even. */ - cpio->entry_bytes_remaining = le4(header + bin_filesize_offset); + cpio->entry_bytes_remaining = le32dec(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); @@ -942,7 +965,7 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } @@ -950,24 +973,24 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, /* Parse out binary fields. */ header = (const unsigned char *)h; - archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]); - archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]); - archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]); + archive_entry_set_dev(entry, archive_be16dec(header + bin_dev_offset)); + archive_entry_set_ino(entry, archive_be16dec(header + bin_ino_offset)); + archive_entry_set_mode(entry, archive_be16dec(header + bin_mode_offset)); if (cpio->option_pwb) { /* turn off random bits left over from V6 inode */ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777); if ((archive_entry_mode(entry) & AE_IFMT) == 0) archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG); } - archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]); - archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]); - archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]); - archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]); - archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0); - *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1]; + archive_entry_set_uid(entry, archive_be16dec(header + bin_uid_offset)); + archive_entry_set_gid(entry, archive_be16dec(header + bin_gid_offset)); + archive_entry_set_nlink(entry, archive_be16dec(header + bin_nlink_offset)); + archive_entry_set_rdev(entry, archive_be16dec(header + bin_rdev_offset)); + archive_entry_set_mtime(entry, be32dec(header + bin_mtime_offset), 0); + *namelength = archive_be16dec(header + bin_namesize_offset); *name_pad = *namelength & 1; /* Pad to even. */ - cpio->entry_bytes_remaining = be4(header + bin_filesize_offset); + cpio->entry_bytes_remaining = be32dec(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); @@ -994,16 +1017,15 @@ archive_read_format_cpio_cleanup(struct archive_read *a) } static int64_t -le4(const unsigned char *p) +le32dec(const unsigned char *p) { - return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8)); + return ((int64_t)archive_le16dec(p) << 16) | archive_le16dec(p + 2); } - static int64_t -be4(const unsigned char *p) +be32dec(const unsigned char *p) { - return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); + return ((int64_t)archive_be16dec(p) << 16) | archive_be16dec(p + 2); } /* @@ -1051,7 +1073,7 @@ atol16u(const char *p, unsigned char_cnt) else if (*p >= '0' && *p <= '9') digit = *p - '0'; else - return ((int64_t)l); + return (l); p++; l <<= 4; l |= digit; @@ -1102,20 +1124,32 @@ record_hardlink(struct archive_read *a, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } - if (cpio->links_head != NULL) - cpio->links_head->previous = le; - le->next = cpio->links_head; - le->previous = NULL; - cpio->links_head = le; + + const char *pathname = archive_entry_pathname(entry); + if (pathname == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid hardlink entry with no pathname"); + free(le); + return (ARCHIVE_FATAL); + } + le->dev = dev; le->ino = ino; le->links = archive_entry_nlink(entry) - 1; - le->name = strdup(archive_entry_pathname(entry)); + le->name = strdup(pathname); if (le->name == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); + free(le); return (ARCHIVE_FATAL); } + if (cpio->links_head != NULL) + cpio->links_head->previous = le; + le->next = cpio->links_head; + le->previous = NULL; + cpio->links_head = le; + return (ARCHIVE_OK); } diff --git a/libarchive-clib/c/archive_read_support_format_iso9660.c b/libarchive-clib/c/archive_read_support_format_iso9660.c index c69fcd2..e1df627 100644 --- a/libarchive-clib/c/archive_read_support_format_iso9660.c +++ b/libarchive-clib/c/archive_read_support_format_iso9660.c @@ -268,6 +268,7 @@ struct file_info { uint64_t size; /* File size in bytes. */ uint32_t ce_offset; /* Offset of CE. */ uint32_t ce_size; /* Size of CE. */ + uint64_t ce_processed_end;/* End offset of processed CE. */ char rr_moved; /* Flag to rr_moved. */ char rr_moved_has_re_only; char re; /* Having RRIP "RE" extension. */ @@ -436,7 +437,6 @@ static void parse_rockridge_ZF1(struct file_info *, const unsigned char *, int); static void register_file(struct iso9660 *, struct file_info *); static void release_files(struct iso9660 *); -static unsigned toi(const void *p, int n); static inline void re_add_entry(struct iso9660 *, struct file_info *); static inline struct file_info * re_get_entry(struct iso9660 *); static inline int rede_add_entry(struct file_info *); @@ -1865,7 +1865,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, } name_len = (size_t)isodirrec[DR_name_len_offset]; location = archive_le32dec(isodirrec + DR_extent_offset); - fsize = toi(isodirrec + DR_size_offset, DR_size_size); + fsize = archive_le32dec(isodirrec + DR_size_offset); /* Sanity check that name_len doesn't exceed dr_len. */ if (dr_len - 33 < name_len || name_len == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -2162,7 +2162,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, fprintf(stderr, "\n ** Unrecognized flag: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); - } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) { + } else if (archive_le16dec(isodirrec + DR_volume_sequence_number_offset) != 1) { fprintf(stderr, "\n ** Unrecognized sequence number: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); @@ -2255,9 +2255,10 @@ parse_rockridge(struct archive_read *a, struct file_info *file, */ if (p[1] == 'N') { if (version == 1 && data_length == 16) { - file->rdev = toi(data,4); + file->rdev = archive_le32dec(data); file->rdev <<= 32; - file->rdev |= toi(data + 8, 4); + file->rdev |= + archive_le32dec(data + 8); iso9660->seenRockridge = 1; } } @@ -2272,20 +2273,20 @@ parse_rockridge(struct archive_read *a, struct file_info *file, */ if (version == 1) { if (data_length >= 8) - file->mode - = (__LA_MODE_T)toi(data, 4); + file->mode = (__LA_MODE_T) + archive_le32dec(data); if (data_length >= 16) - file->nlinks - = toi(data + 8, 4); + file->nlinks = + archive_le32dec(data + 8); if (data_length >= 24) - file->uid - = toi(data + 16, 4); + file->uid = + archive_le32dec(data + 16); if (data_length >= 32) - file->gid - = toi(data + 24, 4); + file->gid = + archive_le32dec(data + 24); if (data_length >= 40) - file->number - = toi(data + 32, 4); + file->number = + archive_le32dec(data + 32); iso9660->seenRockridge = 1; } } @@ -2483,6 +2484,7 @@ read_CE(struct archive_read *a, struct iso9660 *iso9660) const unsigned char *b, *p, *end; struct file_info *file; size_t step; + uint64_t ce_start, ce_end; int r; /* Read data which RRIP "CE" extension points. */ @@ -2506,8 +2508,16 @@ read_CE(struct archive_read *a, struct iso9660 *iso9660) "Malformed CE information"); return (ARCHIVE_FATAL); } + ce_start = heap->reqs[0].offset + file->ce_offset; + ce_end = ce_start + file->ce_size; + if (ce_start < file->ce_processed_end) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid parameter in SUSP \"CE\" extension"); + return (ARCHIVE_FATAL); + } p = b + file->ce_offset; end = p + file->ce_size; + file->ce_processed_end = ce_end; next_CE(heap); r = parse_rockridge(a, file, p, end); if (r != ARCHIVE_OK) @@ -3238,17 +3248,6 @@ heap_get_entry(struct heap_queue *heap) } } -static unsigned int -toi(const void *p, int n) -{ - const unsigned char *v = (const unsigned char *)p; - if (n > 1) - return v[0] + 256 * toi(v + 1, n - 1); - if (n == 1) - return v[0]; - return (0); -} - /* * ECMA119/ISO9660 stores multi-byte integers in one of * three different formats: @@ -3493,6 +3492,8 @@ build_pathname_utf16be(unsigned char *p, size_t max, size_t *len, if (file->parent != NULL && file->parent->utf16be_bytes > 0) { if (build_pathname_utf16be(p, max, len, file->parent) != 0) return (-1); + if (*len + 2 > max) + return (-1);/* Path is too long! */ p[*len] = 0; p[*len + 1] = '/'; *len += 2; @@ -3517,26 +3518,24 @@ static void dump_isodirrec(FILE *out, const unsigned char *isodirrec) { fprintf(out, " l %d,", - toi(isodirrec + DR_length_offset, DR_length_size)); + isodirrec[DR_length_offset]); fprintf(out, " a %d,", - toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size)); + isodirrec[DR_ext_attr_length_offset]); fprintf(out, " ext 0x%x,", - toi(isodirrec + DR_extent_offset, DR_extent_size)); + archive_le32dec(isodirrec + DR_extent_offset)); fprintf(out, " s %d,", - toi(isodirrec + DR_size_offset, DR_extent_size)); + archive_le32dec(isodirrec + DR_size_offset)); fprintf(out, " f 0x%x,", - toi(isodirrec + DR_flags_offset, DR_flags_size)); + isodirrec[DR_flags_offset]); fprintf(out, " u %d,", - toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size)); + isodirrec[DR_file_unit_size_offset]); fprintf(out, " ilv %d,", - toi(isodirrec + DR_interleave_offset, DR_interleave_size)); + isodirrec[DR_interleave_offset]); fprintf(out, " seq %d,", - toi(isodirrec + DR_volume_sequence_number_offset, - DR_volume_sequence_number_size)); + archive_le16dec(isodirrec + DR_volume_sequence_number_offset)); fprintf(out, " nl %d:", - toi(isodirrec + DR_name_len_offset, DR_name_len_size)); + isodirrec[DR_name_len_offset]); fprintf(out, " `%.*s'", - toi(isodirrec + DR_name_len_offset, DR_name_len_size), - isodirrec + DR_name_offset); + isodirrec[DR_name_len_offset], isodirrec + DR_name_offset); } #endif diff --git a/libarchive-clib/c/archive_read_support_format_lha.c b/libarchive-clib/c/archive_read_support_format_lha.c index ff6dbb8..b9e6a17 100644 --- a/libarchive-clib/c/archive_read_support_format_lha.c +++ b/libarchive-clib/c/archive_read_support_format_lha.c @@ -107,8 +107,8 @@ struct lzh_dec { unsigned char *bitlen; /* - * Use a index table. It's faster than searching a huffman - * coding tree, which is a binary tree. But a use of a large + * Use an index table. It's faster than searching a huffman + * coding tree, which is a binary tree. But usage of a large * index table causes L1 cache read miss many times. */ #define HTBL_BITS 10 @@ -142,7 +142,6 @@ struct lzh_stream { int64_t total_in; const unsigned char *ref_ptr; int avail_out; - int64_t total_out; struct lzh_dec *ds; }; @@ -239,7 +238,6 @@ static int lha_read_data_none(struct archive_read *, const void **, size_t *, int64_t *); static int lha_read_data_lzh(struct archive_read *, const void **, size_t *, int64_t *); -static void lha_crc16_init(void); static uint16_t lha_crc16(uint16_t, const void *, size_t); static int lzh_decode_init(struct lzh_stream *, const char *); static void lzh_decode_free(struct lzh_stream *); @@ -483,8 +481,6 @@ archive_read_format_lha_read_header(struct archive_read *a, struct archive_mstring conv_buffer; const wchar_t *conv_buffer_p; - lha_crc16_init(); - a->archive.archive_format = ARCHIVE_FORMAT_LHA; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "lha"; @@ -1588,7 +1584,6 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, /* We've initialized decompression for this stream. */ lha->decompress_init = 1; lha->strm.avail_out = 0; - lha->strm.total_out = 0; } /* @@ -1733,30 +1728,86 @@ lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size) return (sum); } -static uint16_t crc16tbl[2][256]; -static void -lha_crc16_init(void) -{ - unsigned int i; - static int crc16init = 0; - - if (crc16init) - return; - crc16init = 1; - - for (i = 0; i < 256; i++) { - unsigned int j; - uint16_t crc = (uint16_t)i; - for (j = 8; j; j--) - crc = (crc >> 1) ^ ((crc & 1) * 0xA001); - crc16tbl[0][i] = crc; - } - - for (i = 0; i < 256; i++) { - crc16tbl[1][i] = (crc16tbl[0][i] >> 8) - ^ crc16tbl[0][crc16tbl[0][i] & 0xff]; +static const uint16_t crc16tbl[2][256] = { + { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, + 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, + 0xc481, 0x0440, 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, + 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, + 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, + 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, + 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, + 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, + 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, + 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, + 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40, + 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, + 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, + 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, + 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, + 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, + 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, + 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, + 0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, + 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0, + 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, + 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, + 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, + 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, + 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, + 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, + 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0, + 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, + 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, + 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, + 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, + 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, + 0x4100, 0x81c1, 0x8081, 0x4040 + }, + { + 0x0000, 0x9001, 0x6001, 0xf000, 0xc002, 0x5003, 0xa003, + 0x3002, 0xc007, 0x5006, 0xa006, 0x3007, 0x0005, 0x9004, + 0x6004, 0xf005, 0xc00d, 0x500c, 0xa00c, 0x300d, 0x000f, + 0x900e, 0x600e, 0xf00f, 0x000a, 0x900b, 0x600b, 0xf00a, + 0xc008, 0x5009, 0xa009, 0x3008, 0xc019, 0x5018, 0xa018, + 0x3019, 0x001b, 0x901a, 0x601a, 0xf01b, 0x001e, 0x901f, + 0x601f, 0xf01e, 0xc01c, 0x501d, 0xa01d, 0x301c, 0x0014, + 0x9015, 0x6015, 0xf014, 0xc016, 0x5017, 0xa017, 0x3016, + 0xc013, 0x5012, 0xa012, 0x3013, 0x0011, 0x9010, 0x6010, + 0xf011, 0xc031, 0x5030, 0xa030, 0x3031, 0x0033, 0x9032, + 0x6032, 0xf033, 0x0036, 0x9037, 0x6037, 0xf036, 0xc034, + 0x5035, 0xa035, 0x3034, 0x003c, 0x903d, 0x603d, 0xf03c, + 0xc03e, 0x503f, 0xa03f, 0x303e, 0xc03b, 0x503a, 0xa03a, + 0x303b, 0x0039, 0x9038, 0x6038, 0xf039, 0x0028, 0x9029, + 0x6029, 0xf028, 0xc02a, 0x502b, 0xa02b, 0x302a, 0xc02f, + 0x502e, 0xa02e, 0x302f, 0x002d, 0x902c, 0x602c, 0xf02d, + 0xc025, 0x5024, 0xa024, 0x3025, 0x0027, 0x9026, 0x6026, + 0xf027, 0x0022, 0x9023, 0x6023, 0xf022, 0xc020, 0x5021, + 0xa021, 0x3020, 0xc061, 0x5060, 0xa060, 0x3061, 0x0063, + 0x9062, 0x6062, 0xf063, 0x0066, 0x9067, 0x6067, 0xf066, + 0xc064, 0x5065, 0xa065, 0x3064, 0x006c, 0x906d, 0x606d, + 0xf06c, 0xc06e, 0x506f, 0xa06f, 0x306e, 0xc06b, 0x506a, + 0xa06a, 0x306b, 0x0069, 0x9068, 0x6068, 0xf069, 0x0078, + 0x9079, 0x6079, 0xf078, 0xc07a, 0x507b, 0xa07b, 0x307a, + 0xc07f, 0x507e, 0xa07e, 0x307f, 0x007d, 0x907c, 0x607c, + 0xf07d, 0xc075, 0x5074, 0xa074, 0x3075, 0x0077, 0x9076, + 0x6076, 0xf077, 0x0072, 0x9073, 0x6073, 0xf072, 0xc070, + 0x5071, 0xa071, 0x3070, 0x0050, 0x9051, 0x6051, 0xf050, + 0xc052, 0x5053, 0xa053, 0x3052, 0xc057, 0x5056, 0xa056, + 0x3057, 0x0055, 0x9054, 0x6054, 0xf055, 0xc05d, 0x505c, + 0xa05c, 0x305d, 0x005f, 0x905e, 0x605e, 0xf05f, 0x005a, + 0x905b, 0x605b, 0xf05a, 0xc058, 0x5059, 0xa059, 0x3058, + 0xc049, 0x5048, 0xa048, 0x3049, 0x004b, 0x904a, 0x604a, + 0xf04b, 0x004e, 0x904f, 0x604f, 0xf04e, 0xc04c, 0x504d, + 0xa04d, 0x304c, 0x0044, 0x9045, 0x6045, 0xf044, 0xc046, + 0x5047, 0xa047, 0x3046, 0xc043, 0x5042, 0xa042, 0x3043, + 0x0041, 0x9040, 0x6040, 0xf041 } -} +}; static uint16_t lha_crc16(uint16_t crc, const void *pp, size_t len) @@ -2084,7 +2135,6 @@ lzh_emit_window(struct lzh_stream *strm, size_t s) { strm->ref_ptr = strm->ds->w_buff; strm->avail_out = (int)s; - strm->total_out += s; } static int diff --git a/libarchive-clib/c/archive_read_support_format_mtree.c b/libarchive-clib/c/archive_read_support_format_mtree.c index 4a5a49c..66e2cb1 100644 --- a/libarchive-clib/c/archive_read_support_format_mtree.c +++ b/libarchive-clib/c/archive_read_support_format_mtree.c @@ -51,6 +51,7 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_private.h" +#include "archive_integer.h" #include "archive_platform_stat.h" #include "archive_private.h" #include "archive_rb.h" @@ -1790,12 +1791,16 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, * 123456789.1 represents 123456789 * seconds and 1 nanosecond. */ if (*val == '.') { + int64_t v; + ++val; - ns = (long)mtree_atol(&val, 10); - if (ns < 0) + v = mtree_atol(&val, 10); + if (v < 0) ns = 0; - else if (ns > 999999999) + else if (v > 999999999) ns = 999999999; + else + ns = (long)v; } if (m > my_time_t_max) m = my_time_t_max; @@ -2029,9 +2034,9 @@ parsedigit(char c) if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') - return c - 'a'; + return 10 + c - 'a'; else if (c >= 'A' && c <= 'F') - return c - 'A'; + return 10 + c - 'A'; else return -1; } @@ -2044,8 +2049,8 @@ parsedigit(char c) static int64_t mtree_atol(char **p, int base) { - int64_t l, limit; - int digit, last_digit_limit; + int64_t l; + int digit; if (base == 0) { if (**p != '0') @@ -2059,29 +2064,24 @@ mtree_atol(char **p, int base) } if (**p == '-') { - limit = INT64_MIN / base; - last_digit_limit = -(INT64_MIN % base); ++(*p); l = 0; digit = parsedigit(**p); while (digit >= 0 && digit < base) { - if (l < limit || (l == limit && digit >= last_digit_limit)) + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_sub_i64(&l, l, digit)) return INT64_MIN; - l = (l * base) - digit; digit = parsedigit(*++(*p)); } return l; } else { - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; - l = 0; digit = parsedigit(**p); while (digit >= 0 && digit < base) { - if (l > limit || (l == limit && digit > last_digit_limit)) + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_add_i64(&l, l, digit)) return INT64_MAX; - l = (l * base) + digit; digit = parsedigit(*++(*p)); } return l; @@ -2100,8 +2100,7 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t bytes_read; ssize_t total_size = 0; ssize_t find_off = 0; - const void *t; - void *nl; + const void *nl, *t; char *u; /* Accumulate line in a line buffer. */ diff --git a/libarchive-clib/c/archive_read_support_format_rar.c b/libarchive-clib/c/archive_read_support_format_rar.c index 0ed2540..803b2e2 100644 --- a/libarchive-clib/c/archive_read_support_format_rar.c +++ b/libarchive-clib/c/archive_read_support_format_rar.c @@ -154,7 +154,7 @@ #define UNP_BUFFER_SIZE (128 * 1024) /* Define this here for non-Windows platforms */ -#if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)) +#ifndef FILE_ATTRIBUTE_DIRECTORY #define FILE_ATTRIBUTE_DIRECTORY 0x10 #endif @@ -1142,7 +1142,7 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported compression method for RAR file"); - ret = ARCHIVE_FATAL; + ret = ARCHIVE_FAILED; break; } return (ret); @@ -1513,27 +1513,23 @@ read_header(struct archive_read *a, struct archive_entry *entry, * consumed at the end. */ if (head_type == NEWSUB_HEAD) { - size_t distance = p - (const char *)h; if (rar->packed_size > INT64_MAX - header_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Extended header size too large"); + "Invalid RAR file: Overlarge extended header"); return (ARCHIVE_FATAL); } - header_size += rar->packed_size; - if ((uintmax_t)header_size > SIZE_MAX) { + if (__archive_read_consume(a, header_size + rar->packed_size - 7) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unable to read extended header data"); + "Invalid RAR file: Cannot read extended header data"); return (ARCHIVE_FATAL); } - /* Make sure we have the extended data. */ - if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to read extended header data"); - return (ARCHIVE_FATAL); - } - p = h; - endp = p + header_size - 7; - p += distance; + + /* + * NEWSUB records are metadata-only in this reader. The block header + * has already been validated, so it is safe to skip exactly the + * remaining header bytes and the associated data payload. + */ + return ret; } filename_size = archive_le16dec(file_header.name_size); @@ -1829,10 +1825,6 @@ read_header(struct archive_read *a, struct archive_entry *entry, rar->ppmd_valid = rar->ppmd_eod = 0; rar->filters.filterstart = INT64_MAX; - /* Don't set any archive entries for non-file header types */ - if (head_type == NEWSUB_HEAD) - return ret; - archive_entry_set_mtime(entry, rar->mtime, rar->mnsec); archive_entry_set_ctime(entry, rar->ctime, rar->cnsec); archive_entry_set_atime(entry, rar->atime, rar->ansec); @@ -2026,7 +2018,7 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size, #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); #endif } rar->entry_eof = 1; @@ -2038,7 +2030,7 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } *size = bytes_avail; @@ -2058,7 +2050,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, int64_t *offset, size_t looper) { if (looper++ > MAX_COMPRESS_DEPTH) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); struct rar *rar; int64_t start, end; @@ -2069,7 +2061,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, do { if (!rar->valid) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); if (rar->filters.bytes_ready > 0) { @@ -2121,7 +2113,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); #endif } rar->entry_eof = 1; @@ -2155,7 +2147,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, if (rar->filters.lastend == rar->filters.filterstart) { if (!run_filters(a)) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); continue; } @@ -2172,7 +2164,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if(sym != rar->ppmd_escape) { @@ -2186,7 +2178,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } switch(code) @@ -2213,7 +2205,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } lzss_offset |= code << (i * 8); } @@ -2222,7 +2214,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } lzss_emit_match(rar, lzss_offset + 2, length + 32); rar->bytes_uncopied += length + 32; @@ -2234,7 +2226,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } lzss_emit_match(rar, 1, length + 4); rar->bytes_uncopied += length + 4; @@ -2280,7 +2272,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, * what we would do to solve it. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Internal error extracting RAR file"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } } if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset)) @@ -2363,7 +2355,7 @@ parse_codes(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } /* Make sure ppmd7_context is freed before Ppmd7_Construct @@ -2379,7 +2371,7 @@ parse_codes(struct archive_read *a) if (rar->dictionary_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid zero dictionary size"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context, @@ -2393,7 +2385,7 @@ parse_codes(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder); rar->ppmd_valid = 1; @@ -2403,13 +2395,13 @@ parse_codes(struct archive_read *a) if (!rar->ppmd_valid) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid PPMd sequence"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } } } @@ -2417,6 +2409,13 @@ parse_codes(struct archive_read *a) { rar_br_consume(br, 1); + /* + * Low-distance repeat state belongs to the current LZ table and + * must not be reused after starting a new table. + */ + rar->lastlowoffset = 0; + rar->numlowoffsetrepeats = 0; + /* Keep existing table flag */ if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; @@ -2459,7 +2458,7 @@ parse_codes(struct archive_read *a) if ((val = read_next_symbol(a, &precode)) < 0) { free(precode.tree); free(precode.table); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (val < 16) { @@ -2474,7 +2473,7 @@ parse_codes(struct archive_read *a) free(precode.table); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Internal error extracting RAR file"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if(val == 16) { @@ -2564,7 +2563,7 @@ parse_codes(struct archive_read *a) if (new_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Zero window size is invalid"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } new_window = realloc(rar->lzss.window, new_size); if (new_window == NULL) { @@ -2584,7 +2583,7 @@ parse_codes(struct archive_read *a) archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } static void @@ -2700,7 +2699,7 @@ create_code(struct archive_read *a, struct huffman_code *code, { if (lengths[j] != i) continue; if (add_value(a, code, j, codebits, i) != ARCHIVE_OK) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); codebits++; if (--symbolsleft <= 0) break; @@ -2752,7 +2751,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Prefix found"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } /* @@ -2811,7 +2810,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Prefix found"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } /* Set leaf value */ @@ -2850,6 +2849,10 @@ make_table(struct archive_read *a, struct huffman_code *code) code->tablesize = code->maxlength; code->table = calloc(((size_t)1U) << code->tablesize, sizeof(*code->table)); + if (code->table == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } return make_table_recurse(a, code, 0, code->table, 0, code->tablesize); } @@ -2865,13 +2868,13 @@ make_table_recurse(struct archive_read *a, struct huffman_code *code, int node, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Huffman tree was not created"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (node < 0 || node >= code->numentries) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid location to Huffman tree specified"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } currtablesize = 1 << (maxdepth - depth); @@ -3134,11 +3137,11 @@ expand(struct archive_read *a, int64_t *end) archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); bad_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } static int @@ -3153,12 +3156,12 @@ copy_from_lzss_window(struct archive_read *a, uint8_t *buffer, if (length > lzss_size(&rar->lzss)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (firstpart < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (firstpart < length) { memcpy(buffer, &rar->lzss.window[windowoffs], firstpart); @@ -3200,7 +3203,7 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, if (firstpart < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if ((size_t)firstpart < length) { memcpy(&rar->unp_buffer[rar->unp_offset], @@ -3224,7 +3227,7 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } static const void * @@ -3556,7 +3559,13 @@ compile_program(const uint8_t *bytes, size_t length) if (membr_bits(&br, 1)) { - prog->staticdatalen = membr_next_rarvm_number(&br) + 1; + uint32_t staticdatalen = membr_next_rarvm_number(&br); + if (staticdatalen >= VM_MEMORY_SIZE) + { + delete_program_code(prog); + return NULL; + } + prog->staticdatalen = staticdatalen + 1; prog->staticdata = malloc(prog->staticdatalen); if (!prog->staticdata) { diff --git a/libarchive-clib/c/archive_read_support_format_rar5.c b/libarchive-clib/c/archive_read_support_format_rar5.c index 63dd97b..ae36a75 100644 --- a/libarchive-clib/c/archive_read_support_format_rar5.c +++ b/libarchive-clib/c/archive_read_support_format_rar5.c @@ -81,7 +81,9 @@ * * The array itself is decrypted in `rar5_init` function. */ -static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; +static const unsigned char rar5_signature_xor[] = { + 243, 192, 211, 128, 187, 166, 160, 161 +}; static const size_t g_unpack_window_size = 0x20000; /* These could have been static const's, but they aren't, because of @@ -700,7 +702,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported filter type: 0x%x", (unsigned int)flt->type); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } if(ret != ARCHIVE_OK) { @@ -905,11 +907,10 @@ static inline int get_archive_read(struct archive* a, static int read_ahead(struct archive_read* a, size_t how_many, const uint8_t** ptr) { - ssize_t avail = -1; if(!ptr) return 0; - *ptr = __archive_read_ahead(a, how_many, &avail); + *ptr = __archive_read_ahead(a, how_many, NULL); if(*ptr == NULL) { return 0; } @@ -1046,10 +1047,7 @@ static int read_bits_32(struct archive_read* a, struct rar5* rar, return ARCHIVE_FATAL; } - uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; - bits |= p[rar->bits.in_addr + 1] << 16; - bits |= p[rar->bits.in_addr + 2] << 8; - bits |= p[rar->bits.in_addr + 3]; + uint32_t bits = archive_be32dec(p + rar->bits.in_addr); bits <<= rar->bits.bit_addr; bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); *value = bits; @@ -1066,9 +1064,7 @@ static int read_bits_16(struct archive_read* a, struct rar5* rar, return ARCHIVE_FATAL; } - int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; - bits |= (int) p[rar->bits.in_addr + 1] << 8; - bits |= (int) p[rar->bits.in_addr + 2]; + uint32_t bits = archive_be24dec(p + (unsigned)rar->bits.in_addr); bits >>= (8 - rar->bits.bit_addr); *value = bits & 0xffff; return ARCHIVE_OK; @@ -1790,6 +1786,13 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if(!read_var_sized(a, &data_size, NULL)) return ARCHIVE_EOF; + if(data_size > SSIZE_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "File data size is too large"); + return ARCHIVE_FATAL; + } + rar->file.bytes_remaining = data_size; } else { rar->file.bytes_remaining = 0; @@ -1946,29 +1949,20 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, archive_entry_set_mode(entry, mode); if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) { - char *fflags_text, *ptr; - /* allocate for ",rdonly,hidden,system" */ - fflags_text = malloc(22 * sizeof(*fflags_text)); - if (fflags_text != NULL) { - ptr = fflags_text; - if (file_attr & ATTR_READONLY) { - strcpy(ptr, ",rdonly"); - ptr = ptr + 7; - } - if (file_attr & ATTR_HIDDEN) { - strcpy(ptr, ",hidden"); - ptr = ptr + 7; - } - if (file_attr & ATTR_SYSTEM) { - strcpy(ptr, ",system"); - ptr = ptr + 7; - } - if (ptr > fflags_text) { - archive_entry_copy_fflags_text(entry, - fflags_text + 1); - } - free(fflags_text); - } + char buf[sizeof(",rdonly,hidden,system")]; + char *fflags[3] = { "", "", "" }; + char **flag = fflags; + + if (file_attr & ATTR_READONLY) + *flag++ = ",rdonly"; + if (file_attr & ATTR_HIDDEN) + *flag++ = ",hidden"; + if (file_attr & ATTR_SYSTEM) + *flag++ = ",system"; + + snprintf(buf, sizeof(buf), "%s%s%s", + fflags[0], fflags[1], fflags[2]); + archive_entry_copy_fflags_text(entry, buf + 1); } } else if(host_os == HOST_UNIX) { /* Host OS is Unix */ @@ -2274,6 +2268,33 @@ static int scan_for_signature(struct archive_read* a); * block. */ +/* + * A header that carries no file data (HEAD_MAIN, or an unknown block + * flagged HFL_SKIP_IF_UNKNOWN) may leave bytes in its body that the + * sub-parser did not read. Skip them before returning ARCHIVE_RETRY, + * otherwise rar5_read_header() re-parses the same block region O(N) + * times instead of O(1), letting a crafted RAR5 file stall the reader + * (GHSA-9h2c-464f-j3hj). + * + * Safe because read_ahead(a, hdr_size, &p) pre-loaded the whole block + * into one contiguous buffer with no compaction until we return, so + * body_start stays valid and (cur - body_start) is the exact number of + * body bytes consumed so far. + */ +static void +rar5_skip_remaining_block(struct archive_read* a, + const uint8_t* body_start, size_t raw_hdr_size) +{ + const uint8_t* cur; + + if(read_ahead(a, 1, &cur)) { + size_t body_used = (size_t)(cur - body_start); + + if(body_used < raw_hdr_size) + (void)consume(a, raw_hdr_size - body_used); + } +} + static int process_base_block(struct archive_read* a, struct archive_entry* entry) { @@ -2285,6 +2306,7 @@ static int process_base_block(struct archive_read* a, size_t header_id = 0; size_t header_flags = 0; const uint8_t* p; + const uint8_t* body_start; int ret; enum HEADER_TYPE { @@ -2346,6 +2368,10 @@ static int process_base_block(struct archive_read* a, #endif } + /* Remember the first byte of the block body so we can later skip + * any bytes the sub-parser leaves unconsumed. */ + body_start = p + hdr_size_len; + /* If the checksum is OK, we proceed with parsing. */ if(ARCHIVE_OK != consume(a, hdr_size_len)) { return ARCHIVE_EOF; @@ -2371,8 +2397,11 @@ static int process_base_block(struct archive_read* a, /* Main header doesn't have any files in it, so it's * pointless to return to the caller. Retry to next * header, which should be HEAD_FILE/HEAD_SERVICE. */ - if(ret == ARCHIVE_OK) + if(ret == ARCHIVE_OK) { + rar5_skip_remaining_block(a, body_start, + raw_hdr_size); return ARCHIVE_RETRY; + } return ret; case HEAD_SERVICE: @@ -2432,6 +2461,8 @@ static int process_base_block(struct archive_read* a, /* If the block is marked as 'skip if unknown', * do as the flag says: skip the block * instead on failing on it. */ + rar5_skip_remaining_block(a, body_start, + raw_hdr_size); return ARCHIVE_RETRY; } } @@ -2561,19 +2592,23 @@ static int rar5_read_header(struct archive_read *a, return ret; } -static void init_unpack(struct rar5* rar) { +static int init_unpack(struct rar5* rar) { rar->file.calculated_crc32 = 0; init_window_mask(rar); free(rar->cstate.window_buf); free(rar->cstate.filtered_buf); + rar->cstate.window_buf = NULL; + rar->cstate.filtered_buf = NULL; + if(rar->cstate.window_size > 0) { rar->cstate.window_buf = calloc(1, rar->cstate.window_size); + if(rar->cstate.window_buf == NULL) + return ARCHIVE_FATAL; rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); - } else { - rar->cstate.window_buf = NULL; - rar->cstate.filtered_buf = NULL; + if(rar->cstate.filtered_buf == NULL) + return ARCHIVE_FATAL; } clear_data_ready_stack(rar); @@ -2586,6 +2621,7 @@ static void init_unpack(struct rar5* rar) { memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); + return ARCHIVE_OK; } static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { @@ -2647,6 +2683,15 @@ static int create_decode_tables(uint8_t* bit_length, upper_limit <<= 1; } + /* Verify the code-length distribution is not over-subscribed. + * After the loop above, upper_limit == sum(lc[i] * 2^(16-i)). + * For a valid prefix-free code this must be <= 2^16 = 65536. + * An over-subscribed table (> 65536) cannot produce a valid + * decode table and must be rejected. */ + if(upper_limit > 65536) { + return ARCHIVE_FAILED; + } + memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); for(i = 0; i < size; i++) { @@ -2748,7 +2793,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated data in huffman tables"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } value = (p[i] & nibble_mask) >> nibble_shift; @@ -2794,7 +2839,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } for(i = 0; i < HUFF_TABLE_SIZE;) { @@ -2805,7 +2850,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } if(num < 16) { @@ -2839,7 +2884,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, ARCHIVE_ERRNO_FILE_FORMAT, "Unexpected error when decoding " "huffman tables"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } } else { /* other codes: fill with zeroes `n` times */ @@ -2867,7 +2912,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create literal table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } idx += HUFF_NC; @@ -2876,7 +2921,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create distance table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } idx += HUFF_DC; @@ -2885,7 +2930,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create lower bits of distances table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } idx += HUFF_LDC; @@ -2894,7 +2939,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create repeating distances table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } return ARCHIVE_OK; @@ -2912,7 +2957,7 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported block header size (was %d, max is 2)", bf_byte_count(hdr)); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } /* This should probably use bit reader interface in order to be more @@ -2956,7 +3001,7 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p, "Block checksum error: got 0x%x, expected 0x%x", hdr->block_cksum, calculated_cksum); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; #endif } @@ -3037,20 +3082,17 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) { filter_type >>= 13; skip_bits(rar, 3); - /* Perform some sanity checks on this filter parameters. Note that we - * allow only DELTA, E8/E9 and ARM filters here, because rest of - * filters are not used in RARv5. */ + /* Perform some sanity checks on this filter parameters. */ if(block_length < 4 || block_length > 0x400000 || - filter_type > FILTER_ARM || !is_valid_filter_block_start(rar, block_start) || (rar->cstate.window_size > 0 && (ssize_t)block_length > rar->cstate.window_size >> 1)) { archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filter encountered"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } /* Allocate a new filter. */ @@ -3203,7 +3245,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the code length"); - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, @@ -3213,7 +3255,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the distance slot"); - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } if(dist_slot < 4) { @@ -3258,7 +3300,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { "Failed to decode the " "distance slot"); - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } if(dist >= INT_MAX - low_dist - 1) { @@ -3268,7 +3310,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { ARCHIVE_ERRNO_FILE_FORMAT, "Distance pointer " "overflow"); - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } dist += low_dist; @@ -3303,7 +3345,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; continue; } else if(num == 256) { @@ -3319,7 +3361,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { rar->cstate.last_len, rar->cstate.dist_cache[0])) { - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } } @@ -3334,18 +3376,18 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, &len_slot)) { - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } len = decode_code_length(a, rar, p, len_slot); if (len == -1) { - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) - return ARCHIVE_FATAL; + return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; continue; } @@ -3507,6 +3549,7 @@ static int merge_block(struct archive_read* a, ssize_t block_size, if(!rar->vol.push_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a merge block buffer"); + rar->cstate.switch_multivolume = 0; return ARCHIVE_FATAL; } @@ -3524,15 +3567,21 @@ static int merge_block(struct archive_read* a, ssize_t block_size, cur_block_size = rar5_min(rar->file.bytes_remaining, block_size - partial_offset); - if(cur_block_size == 0) { + if(cur_block_size < 1) { + /* bytes_remaining is less than 1 at the wrong point in + * the merge loop, indicating corrupt volume + * accounting. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Encountered block size == 0 during block merge"); + "Encountered invalid block size during block merge"); + rar->cstate.switch_multivolume = 0; return ARCHIVE_FATAL; } - if(!read_ahead(a, cur_block_size, &lp)) + if(!read_ahead(a, cur_block_size, &lp)) { + rar->cstate.switch_multivolume = 0; return ARCHIVE_EOF; + } /* Sanity check; there should never be a situation where this * function reads more data than the block's size. */ @@ -3540,6 +3589,7 @@ static int merge_block(struct archive_read* a, ssize_t block_size, archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Consumed too much data when merging blocks"); + rar->cstate.switch_multivolume = 0; return ARCHIVE_FATAL; } @@ -3549,8 +3599,12 @@ static int merge_block(struct archive_read* a, ssize_t block_size, memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); /* Advance the stream read pointer by this block chunk size. */ - if(ARCHIVE_OK != consume(a, cur_block_size)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, cur_block_size)) { + /* Data was copied but stream pointer didn't advance; + * stream position is unrecoverable. */ + rar->cstate.switch_multivolume = 0; + return ARCHIVE_FATAL; + } /* Update the pointers. `partial_offset` contains information * about the sum of merged block chunks. */ @@ -3571,6 +3625,7 @@ static int merge_block(struct archive_read* a, ssize_t block_size, ret = advance_multivolume(a); rar->merge_mode--; if(ret != ARCHIVE_OK) { + rar->cstate.switch_multivolume = 0; return ret; } } @@ -3627,6 +3682,15 @@ static int process_block(struct archive_read* a) { to_skip = sizeof(struct compressed_block_header) + bf_byte_count(&rar->last_block_hdr) + 1; + /* If the block header's to_skip value exceeds the declared + * remaining data, the archive is malformed. */ + if(to_skip > rar->file.bytes_remaining) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Block header size exceeds remaining file data"); + return ARCHIVE_FATAL; + } + if(ARCHIVE_OK != consume(a, to_skip)) return ARCHIVE_EOF; @@ -3863,7 +3927,8 @@ static int do_uncompress_file(struct archive_read* a) { /* Don't perform full context reinitialization if we're * processing a solid archive. */ if(!rar->main.solid || !rar->cstate.window_buf) { - init_unpack(rar); + if((ret = init_unpack(rar)) != ARCHIVE_OK) + return ret; } rar->cstate.initialized = 1; @@ -3876,7 +3941,7 @@ static int do_uncompress_file(struct archive_read* a) { "Invalid window size declaration in this file"); /* This should never happen in valid files. */ - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } if(rar->cstate.all_filters_applied == 1) { @@ -3887,7 +3952,7 @@ static int do_uncompress_file(struct archive_read* a) { * files). */ while(1) { ret = process_block(a); - if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) + if(ret != ARCHIVE_OK) return ret; if(rar->cstate.last_write_ptr == @@ -3913,12 +3978,10 @@ static int do_uncompress_file(struct archive_read* a) { ret = apply_filters(a); if(ret == ARCHIVE_RETRY) { return ARCHIVE_OK; - } else if(ret == ARCHIVE_FATAL) { - return ARCHIVE_FATAL; + } else if(ret != ARCHIVE_OK) { + return ret; } - /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ - if(cdeque_size(&rar->cstate.filters) > 0) { /* Check if we can write something before hitting first * filter. */ @@ -4131,7 +4194,7 @@ static int verify_checksums(struct archive_read* a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: CRC32"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; #endif } else { DEBUG_CODE { @@ -4166,7 +4229,7 @@ static int verify_checksums(struct archive_read* a) { ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: BLAKE2"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; #endif } } @@ -4207,7 +4270,7 @@ static int rar5_read_data(struct archive_read *a, const void **buff, if (rar->headers_are_encrypted || rar->cstate.data_encrypted) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Reading encrypted data is not currently supported"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } if(rar->file.dir > 0) { diff --git a/libarchive-clib/c/archive_read_support_format_tar.c b/libarchive-clib/c/archive_read_support_format_tar.c index 2979492..ac3dd44 100644 --- a/libarchive-clib/c/archive_read_support_format_tar.c +++ b/libarchive-clib/c/archive_read_support_format_tar.c @@ -42,6 +42,7 @@ #include "archive_acl_private.h" /* For ACL parsing routines. */ #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -153,6 +154,8 @@ struct tar { int compat_2x; int process_mac_extensions; int read_concatenated_archives; + int default_inode; + int default_dev; }; /* Track which size fields were present in the headers */ @@ -248,6 +251,36 @@ static const size_t fflags_limit = 512; /* Longest fflags */ static const size_t acl_limit = 131072; /* Longest textual ACL: 128kiB */ static const int64_t entry_limit = 0xfffffffffffffffLL; /* 2^60 bytes = 1 ExbiByte */ +/* + * There's no standard for TIME_T_MAX. So we compute it + * here. TODO: Move this to configure time, but be careful + * about cross-compile environments. + */ +static time_t +get_time_t_max(void) +{ +#if defined(TIME_T_MAX) + return TIME_T_MAX; +#else + /* 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; + } + } +#endif +} + int archive_read_support_format_gnutar(struct archive *a) { @@ -522,10 +555,6 @@ archive_read_format_tar_read_header(struct archive_read *a, * probably not worthwhile just to support the relatively * obscure tar->cpio conversion case. */ - /* TODO: Move this into `struct tar` to avoid conflicts - * when reading multiple archives */ - static int default_inode; - static int default_dev; struct tar *tar; const char *p; const wchar_t *wp; @@ -533,16 +562,17 @@ archive_read_format_tar_read_header(struct archive_read *a, size_t l; int64_t unconsumed = 0; + tar = (struct tar *)(a->format->data); + /* Assign default device/inode values. */ - archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */ - archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */ + archive_entry_set_dev(entry, 1 + tar->default_dev); /* Don't use zero. */ + archive_entry_set_ino(entry, ++tar->default_inode); /* Don't use zero. */ /* Limit generated st_ino number to 16 bits. */ - if (default_inode >= 0xffff) { - ++default_dev; - default_inode = 0; + if (tar->default_inode >= 0xffff) { + ++tar->default_dev; + tar->default_inode = 0; } - tar = (struct tar *)(a->format->data); tar->entry_offset = 0; gnu_clear_sparse_list(tar); tar->size_fields = 0; /* We don't have any size info yet */ @@ -586,16 +616,16 @@ archive_read_format_tar_read_header(struct archive_read *a, * directory: This is needed for certain old tar * variants and even for some broken newer ones. */ - if ((wp = archive_entry_pathname_w(entry)) != NULL) { - l = wcslen(wp); - if (l > 0 && wp[l - 1] == L'/') { + if ((p = archive_entry_pathname(entry)) != NULL) { + l = strlen(p); + if (l > 0 && p[l - 1] == '/') { archive_entry_set_filetype(entry, AE_IFDIR); tar->entry_bytes_remaining = 0; tar->entry_padding = 0; } - } else if ((p = archive_entry_pathname(entry)) != NULL) { - l = strlen(p); - if (l > 0 && p[l - 1] == '/') { + } else if ((wp = archive_entry_pathname_w(entry)) != NULL) { + l = wcslen(wp); + if (l > 0 && wp[l - 1] == L'/') { archive_entry_set_filetype(entry, AE_IFDIR); tar->entry_bytes_remaining = 0; tar->entry_padding = 0; @@ -1369,7 +1399,12 @@ header_common(struct archive_read *a, struct tar *tar, archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); } if (!archive_entry_mtime_is_set(entry)) { - archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); + int64_t t64 = tar_atol(header->mtime, sizeof(header->mtime)); + time_t t = (time_t)t64; + if ((int64_t)t != t64) { /* time_t overflowed */ + t = get_time_t_max(); + } + archive_entry_set_mtime(entry, t, 0); } /* Reconcile the size info. */ @@ -1782,32 +1817,42 @@ header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; - struct archive_string as; int err = ARCHIVE_OK, r; header = (const struct archive_entry_header_ustar *)h; - /* Copy name into an internal buffer to ensure null-termination. */ + /* + * The name field is fixed-width and may not be NUL-terminated. + * Use a temporary string only when prefix/name joining is required. + */ const char *existing_pathname = archive_entry_pathname(entry); const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry); if ((existing_pathname == NULL || existing_pathname[0] == '\0') && (existing_wcs_pathname == NULL || existing_wcs_pathname[0] == '\0')) { + struct archive_string as; + const char *pathname; + size_t pathname_length; + archive_string_init(&as); if (header->prefix[0]) { archive_strncpy(&as, header->prefix, sizeof(header->prefix)); if (as.s[archive_strlen(&as) - 1] != '/') archive_strappend_char(&as, '/'); archive_strncat(&as, header->name, sizeof(header->name)); + pathname = as.s; + pathname_length = archive_strlen(&as); } else { - archive_strncpy(&as, header->name, sizeof(header->name)); + pathname = header->name; + pathname_length = sizeof(header->name); } - if (archive_entry_copy_pathname_l(entry, as.s, archive_strlen(&as), - tar->sconv) != 0) { + r = archive_entry_copy_pathname_l(entry, pathname, + pathname_length, tar->sconv); + archive_string_free(&as); + if (r != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } - archive_string_free(&as); } /* Handle rest of common fields. */ @@ -2009,6 +2054,7 @@ header_pax_extension(struct archive_read *a, struct tar *tar, /* Consume size, name, and `=` */ *unconsumed += p - attr_start; if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) { + archive_string_free(&attr_name); return (ARCHIVE_FATAL); } @@ -2016,6 +2062,7 @@ header_pax_extension(struct archive_read *a, struct tar *tar, archive_set_error(&a->archive, EINVAL, "Malformed pax attributes"); *unconsumed += ext_size + ext_padding; + archive_string_free(&attr_name); return (ARCHIVE_WARN); } @@ -2266,7 +2313,7 @@ pax_attribute_SCHILY_acl(struct archive_read *a, struct tar *tar, } static int -pax_attribute_read_time(struct archive_read *a, size_t value_length, int64_t *ps, long *pn, int64_t *unconsumed) { +pax_attribute_read_time(struct archive_read *a, size_t value_length, __LA_TIME_T *ps, long *pn, int64_t *unconsumed) { struct archive_string as; int r; @@ -2286,12 +2333,16 @@ pax_attribute_read_time(struct archive_read *a, size_t value_length, int64_t *ps return (r); } - pax_time(as.s, archive_strlen(&as), ps, pn); + int64_t sec = 0; + pax_time(as.s, archive_strlen(&as), &sec, pn); archive_string_free(&as); - if (*ps == INT64_MIN) { + + if (sec == INT64_MIN) { *ps = 0; *pn = 0; return (ARCHIVE_WARN); + } else { + *ps = (__LA_TIME_T)sec; } return (ARCHIVE_OK); } @@ -2348,7 +2399,6 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent int64_t t; long n; const char *p; - ssize_t bytes_read; int err = ARCHIVE_OK; switch (key[0]) { @@ -2432,7 +2482,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent (unsigned long long)sparse_map_limit); err = ARCHIVE_FAILED; } else { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2507,15 +2557,20 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent */ if (key_length == 12 && memcmp(key, "creationtime", 12) == 0) { /* LIBARCHIVE.creationtime */ - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_birthtime(entry, t, n); + __LA_TIME_T sec = 0; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_birthtime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax creationtime"); } return (err); } else if (key_length == 11 && memcmp(key, "symlinktype", 11) == 0) { /* LIBARCHIVE.symlinktype */ if (value_length < 16) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " @@ -2549,7 +2604,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent if (value_length > xattr_limit) { err = ARCHIVE_WARN; } else { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2579,7 +2634,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent /* TODO: Should this be FAILED instead? */ err = ARCHIVE_WARN; } else { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2632,7 +2687,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent } else if (key_length == 6 && memcmp(key, "fflags", 6) == 0) { if (value_length < fflags_limit) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { /* Truncated archive */ archive_set_error(&a->archive, EINVAL, @@ -2679,7 +2734,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent key_length -= 6; key += 6; if (value_length < xattr_limit) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2709,7 +2764,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent if (key_length == 9 && memcmp(key, "holesdata", 9) == 0) { /* SUN.holesdata */ if (value_length < sparse_map_limit) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2736,16 +2791,26 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent break; case 'a': if (key_length == 5 && memcmp(key, "atime", 5) == 0) { - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_atime(entry, t, n); + __LA_TIME_T sec = 0; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_atime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax atime"); } return (err); } break; case 'c': if (key_length == 5 && memcmp(key, "ctime", 5) == 0) { - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_ctime(entry, t, n); + __LA_TIME_T sec = 0; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_ctime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax ctime"); } return (err); } else if (key_length == 7 && memcmp(key, "charset", 7) == 0) { @@ -2773,7 +2838,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent case 'h': if (key_length == 10 && memcmp(key, "hdrcharset", 10) == 0) { if (value_length < 64) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " @@ -2817,8 +2882,13 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent break; case 'm': if (key_length == 5 && memcmp(key, "mtime", 5) == 0) { - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_mtime(entry, t, n); + __LA_TIME_T sec; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_mtime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax mtime"); } return (err); } @@ -2885,7 +2955,8 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent /* * Parse a decimal time value, which may include a fractional portion * - * Sets ps to INT64_MIN on error. + * Sets ps to INT64_MIN on error, including syntax issues such as non-digits, + * or a time value that's outside the range of time_t. */ static void pax_time(const char *p, size_t length, int64_t *ps, long *pn) @@ -2894,10 +2965,6 @@ pax_time(const char *p, size_t length, int64_t *ps, long *pn) int64_t s; unsigned long l; int sign; - int64_t limit, last_digit_limit; - - limit = INT64_MAX / 10; - last_digit_limit = INT64_MAX % 10; if (length <= 0) { *ps = 0; @@ -2913,34 +2980,73 @@ pax_time(const char *p, size_t length, int64_t *ps, long *pn) } while (length > 0 && *p >= '0' && *p <= '9') { digit = *p - '0'; - if (s > limit || - (s == limit && digit > last_digit_limit)) { + if (archive_ckd_mul_i64(&s, s, 10) || + archive_ckd_add_i64(&s, s, digit)) { *ps = INT64_MIN; *pn = 0; return; } - s = (s * 10) + digit; ++p; --length; } *ps = s * sign; +#if ARCHIVE_VERSION_NUMBER < 4000000 + /* Libarchive 4.0 will have __LA_TIME_T == int64_t, so + this will be unnecessary. */ + /* Test whether it overflows __LA_TIME_T */ + __LA_TIME_T sec = (__LA_TIME_T)*ps; + if ((int64_t)sec != *ps) { + *ps = INT64_MIN; + *pn = 0; + return; + } +#endif + /* Calculate nanoseconds. */ *pn = 0; - if (length <= 0 || *p != '.') + if (length <= 0) { return; + } + + /* Skip `.` */ + if (*p != '.') { + *ps = INT64_MIN; + *pn = 0; + return; + } + ++p; + --length; l = 100000000UL; do { + if (length <= 0) { + return; + } + if (*p >= '0' && *p <= '9') { + *pn += (*p - '0') * l; + } else { + *ps = INT64_MIN; + *pn = 0; + return; + } ++p; --length; - if (length > 0 && *p >= '0' && *p <= '9') - *pn += (*p - '0') * l; - else - break; } while (l /= 10); + + /* Ignore resolution beyond nanoseconds, + but verify it's all decimal digits. */ + while (length > 0) { + if (*p < '0' || *p > '9') { + *ps = INT64_MIN; + *pn = 0; + return; + } + ++p; + --length; + } } /* @@ -3103,7 +3209,6 @@ static int gnu_sparse_old_read(struct archive_read *a, struct tar *tar, const struct archive_entry_header_gnutar *header, int64_t *unconsumed) { - ssize_t bytes_read; const void *data; struct extended { struct gnu_sparse sparse[21]; @@ -3121,7 +3226,7 @@ gnu_sparse_old_read(struct archive_read *a, struct tar *tar, if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) { return (ARCHIVE_FATAL); } - data = __archive_read_ahead(a, 512, &bytes_read); + data = __archive_read_ahead(a, 512, NULL); if (data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " @@ -3239,14 +3344,12 @@ static int64_t gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, int64_t *remaining, int64_t *unconsumed) { - int64_t l, limit, last_digit_limit; + int64_t l; const char *p; ssize_t bytes_read; int base, digit; base = 10; - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; /* * Skip any lines starting with '#'; GNU tar specs @@ -3267,10 +3370,10 @@ gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, if (*p < '0' || *p >= '0' + base) return (ARCHIVE_WARN); digit = *p - '0'; - if (l > limit || (l == limit && digit > last_digit_limit)) + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_add_i64(&l, l, digit)) { l = INT64_MAX; /* Truncate on overflow. */ - else - l = (l * base) + digit; + } p++; bytes_read--; } @@ -3416,13 +3519,9 @@ tar_atol(const char *p, size_t char_cnt) static int64_t tar_atol_base_n(const char *p, size_t char_cnt, int base) { - int64_t l, maxval, limit, last_digit_limit; + int64_t l; int digit, sign; - maxval = INT64_MAX; - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; - /* the pointer will not be dereferenced if char_cnt is zero * due to the way the && operator is evaluated. */ @@ -3436,25 +3535,22 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base) sign = -1; p++; char_cnt--; - - maxval = INT64_MIN; - limit = -(INT64_MIN / base); - last_digit_limit = -(INT64_MIN % base); } l = 0; if (char_cnt != 0) { digit = *p - '0'; - while (digit >= 0 && digit < base && char_cnt != 0) { - if (l>limit || (l == limit && digit >= last_digit_limit)) { - return maxval; /* Truncate on overflow. */ + while (digit >= 0 && digit < base && char_cnt != 0) { + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_add_i64(&l, l, sign * digit)) { + /* Truncate on overflow. */ + return sign < 0 ? INT64_MIN : INT64_MAX; } - l = (l * base) + digit; digit = *++p - '0'; char_cnt--; } } - return (sign < 0) ? -l : l; + return l; } static int64_t @@ -3534,9 +3630,8 @@ readline(struct archive_read *a, struct tar *tar, const char **start, { ssize_t bytes_read; ssize_t total_size = 0; - const void *t; + const void *p, *t; const char *s; - void *p; if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) { return (ARCHIVE_FATAL); @@ -3608,24 +3703,20 @@ readline(struct archive_read *a, struct tar *tar, const char **start, static char * base64_decode(const char *s, size_t len, size_t *out_len) { - static const unsigned char digits[64] = { - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', - 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', - 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', - 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', - '4','5','6','7','8','9','+','/' }; - static unsigned char decode_table[128]; + static const unsigned char decode_table[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, + 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, + 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 255, 255, 255, 255, 255 }; char *out, *d; const unsigned char *src = (const unsigned char *)s; - /* If the decode table is not yet initialized, prepare it. */ - if (decode_table[digits[1]] != 1) { - unsigned i; - memset(decode_table, 0xff, sizeof(decode_table)); - for (i = 0; i < sizeof(digits); i++) - decode_table[digits[i]] = i; - } - /* Allocate enough space to hold the entire output. */ /* Note that we may not use all of this... */ out = malloc(len - len / 4 + 1); diff --git a/libarchive-clib/c/archive_read_support_format_xar.c b/libarchive-clib/c/archive_read_support_format_xar.c index 874501f..e61ca0e 100644 --- a/libarchive-clib/c/archive_read_support_format_xar.c +++ b/libarchive-clib/c/archive_read_support_format_xar.c @@ -56,6 +56,7 @@ #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -387,8 +388,7 @@ static int rd_contents_init(struct archive_read *, enum enctype, int, int); static int rd_contents(struct archive_read *, const void **, size_t *, size_t *, uint64_t); -static uint64_t atol10(const char *, size_t); -static int64_t atol8(const char *, size_t); +static int atou64(const char *, size_t, int, uint64_t *); static size_t atohex(unsigned char *, size_t, const char *, size_t); static time_t parse_time(const char *p, size_t n); static int heap_add_entry(struct archive_read *a, @@ -421,7 +421,7 @@ static void unknowntag_end(struct xar *, const char *); static int xml_start(struct archive_read *, const char *, struct xmlattr_list *); static void xml_end(void *, const char *); -static void xml_data(void *, const char *, size_t); +static int xml_data(void *, const char *, size_t); static int xml_parse_file_flags(struct xar *, const char *); static int xml_parse_file_ext2(struct xar *, const char *); #if defined(HAVE_LIBXML_XMLREADER_H) @@ -1071,44 +1071,26 @@ rd_contents(struct archive_read *a, const void **buff, size_t *size, * it does obey locale. */ -static uint64_t -atol10(const char *p, size_t char_cnt) +static int +atou64(const char *p, size_t char_cnt, int base, uint64_t *val) { uint64_t l; - int digit; - - if (char_cnt == 0) - return (0); l = 0; - digit = *p - '0'; - while (digit >= 0 && digit < 10 && char_cnt-- > 0) { - l = (l * 10) + digit; - digit = *++p - '0'; - } - return (l); -} + if (char_cnt > 0) { + int digit; -static int64_t -atol8(const char *p, size_t char_cnt) -{ - int64_t l; - int digit; - - if (char_cnt == 0) - return (0); - - l = 0; - while (char_cnt-- > 0) { - if (*p >= '0' && *p <= '7') - digit = *p - '0'; - else - break; - p++; - l <<= 3; - l |= digit; + digit = *p - '0'; + while (digit >= 0 && digit < base && char_cnt-- > 0) { + if (archive_ckd_mul_u64(&l, l, base) || + archive_ckd_add_u64(&l, l, digit)) + return (ARCHIVE_FATAL); + digit = *++p - '0'; + } } - return (l); + + *val = l; + return (ARCHIVE_OK); } static size_t @@ -1173,48 +1155,42 @@ parse_time(const char *p, size_t n) { struct tm tm; time_t t = 0; - int64_t data; + uint64_t data; memset(&tm, 0, sizeof(tm)); if (n != 20) return (t); - data = atol10(p, 4); - if (data < 1900) + if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1900) return (t); tm.tm_year = (int)data - 1900; p += 4; if (*p++ != '-') return (t); - data = atol10(p, 2); - if (data < 1 || data > 12) + if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1 || data > 12) return (t); tm.tm_mon = (int)data -1; p += 2; if (*p++ != '-') return (t); - data = atol10(p, 2); - if (data < 1 || data > 31) + if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1 || data > 31) return (t); tm.tm_mday = (int)data; p += 2; if (*p++ != 'T') return (t); - data = atol10(p, 2); - if (data < 0 || data > 23) + if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 23) return (t); tm.tm_hour = (int)data; p += 2; if (*p++ != ':') return (t); - data = atol10(p, 2); - if (data < 0 || data > 59) + if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 59) return (t); tm.tm_min = (int)data; p += 2; if (*p++ != ':') return (t); - data = atol10(p, 2); - if (data < 0 || data > 60) + if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 60) return (t); tm.tm_sec = (int)data; #if 0 @@ -1800,12 +1776,20 @@ file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) file->mode = 0777 | AE_IFREG; file->atime = 0; file->mtime = 0; - xar->file = file; xar->xattr = NULL; for (attr = list->first; attr != NULL; attr = attr->next) { - if (strcmp(attr->name, "id") == 0) - file->id = atol10(attr->value, strlen(attr->value)); + if (strcmp(attr->name, "id") == 0) { + int r; + + r = atou64(attr->value, strlen(attr->value), + 10, &file->id); + if (r != ARCHIVE_OK) { + free(file); + return (r); + } + } } + xar->file = file; file->nlink = 1; if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -1822,6 +1806,7 @@ file_free(struct xar_file *file) archive_string_free(&(file->uname)); archive_string_free(&(file->gname)); archive_string_free(&(file->hardlink)); + archive_string_free(&(file->fflags_text)); xattr = file->xattr_list; while (xattr != NULL) { struct xattr *next; @@ -1845,11 +1830,19 @@ xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } - xar->xattr = xattr; for (attr = list->first; attr != NULL; attr = attr->next) { - if (strcmp(attr->name, "id") == 0) - xattr->id = atol10(attr->value, strlen(attr->value)); + if (strcmp(attr->name, "id") == 0) { + int r; + + r = atou64(attr->value, strlen(attr->value), + 10, &xattr->id); + if (r != ARCHIVE_OK) { + free(xattr); + return (r); + } + } } + xar->xattr = xattr; /* Chain to xattr list. */ for (nx = &(xar->file->xattr_list); *nx != NULL; nx = &((*nx)->next)) { @@ -1866,6 +1859,7 @@ static void xattr_free(struct xattr *xattr) { archive_string_free(&(xattr->name)); + archive_string_free(&(xattr->fstype)); free(xattr); } @@ -2065,8 +2059,17 @@ xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) xar->file->hdnext = xar->hdlink_orgs; xar->hdlink_orgs = xar->file; } else { - xar->file->link = (unsigned)atol10(attr->value, - strlen(attr->value)); + uint64_t val; + int r; + r = atou64(attr->value, + strlen(attr->value), 10, &val); + if (r != ARCHIVE_OK) { + return (r); + } + if (val > UINT_MAX) { + return (ARCHIVE_FATAL); + } + xar->file->link = (unsigned)val; if (xar->file->link > 0) if (add_link(a, xar, xar->file) != ARCHIVE_OK) { return (ARCHIVE_FATAL); @@ -2673,11 +2676,13 @@ is_string(const char *known, const char *data, size_t len) return memcmp(data, known, len); } -static void +static int xml_data(void *userData, const char *s, size_t len) { + uint64_t val; struct archive_read *a; struct xar *xar; + int r; a = (struct archive_read *)userData; xar = (struct xar *)(a->format->data); @@ -2685,25 +2690,30 @@ xml_data(void *userData, const char *s, size_t len) #if DEBUG { char buff[1024]; - if (len > (int)(sizeof(buff)-1)) - len = (int)(sizeof(buff)-1); - strncpy(buff, s, len); - buff[len] = 0; - fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff); + size_t dlen = len; + if (dlen > sizeof(buff) - 1) + dlen = sizeof(buff) - 1; + strncpy(buff, s, dlen); + buff[dlen] = 0; + fprintf(stderr, "\tlen=%zu:\"%s\"\n", dlen, buff); } #endif switch (xar->xmlsts) { case TOC_CHECKSUM_OFFSET: - xar->toc_chksum_offset = atol10(s, len); + r = atou64(s, len, 10, &xar->toc_chksum_offset); + if (r != ARCHIVE_OK) + return (r); break; case TOC_CHECKSUM_SIZE: - xar->toc_chksum_size = atol10(s, len); + r = atou64(s, len, 10, &xar->toc_chksum_size); + if (r != ARCHIVE_OK) + return (r); break; default: break; } if (xar->file == NULL) - return; + return (ARCHIVE_OK); switch (xar->xmlsts) { case FILE_NAME: @@ -2752,42 +2762,76 @@ xml_data(void *userData, const char *s, size_t len) xar->file->has |= HAS_TYPE; break; case FILE_INODE: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val > (uint64_t)INT64_MAX) + return (ARCHIVE_FATAL); xar->file->has |= HAS_INO; - xar->file->ino64 = atol10(s, len); + xar->file->ino64 = (int64_t)val; break; case FILE_DEVICE_MAJOR: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (dev_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_DEVMAJOR; - xar->file->devmajor = (dev_t)atol10(s, len); + xar->file->devmajor = (dev_t)val; break; case FILE_DEVICE_MINOR: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (dev_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_DEVMINOR; - xar->file->devminor = (dev_t)atol10(s, len); + xar->file->devminor = (dev_t)val; break; case FILE_DEVICENO: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (dev_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_DEV; - xar->file->dev = (dev_t)atol10(s, len); + xar->file->dev = (dev_t)val; break; case FILE_MODE: + r = atou64(s, len, 8, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (mode_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_MODE; xar->file->mode = - (xar->file->mode & AE_IFMT) | - ((mode_t)(atol8(s, len)) & ~AE_IFMT); + (xar->file->mode & AE_IFMT) | ((mode_t)val & ~AE_IFMT); break; case FILE_GROUP: xar->file->has |= HAS_GID; archive_strncpy(&(xar->file->gname), s, len); break; case FILE_GID: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val > (uint64_t)INT64_MAX) + return (ARCHIVE_FATAL); xar->file->has |= HAS_GID; - xar->file->gid = atol10(s, len); + xar->file->gid = (int64_t)val; break; case FILE_USER: xar->file->has |= HAS_UID; archive_strncpy(&(xar->file->uname), s, len); break; case FILE_UID: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val > (uint64_t)INT64_MAX) + return (ARCHIVE_FATAL); xar->file->has |= HAS_UID; - xar->file->uid = atol10(s, len); + xar->file->uid = (int64_t)val; break; case FILE_CTIME: xar->file->has |= HAS_TIME | HAS_CTIME; @@ -2802,16 +2846,22 @@ xml_data(void *userData, const char *s, size_t len) xar->file->atime = parse_time(s, len); break; case FILE_DATA_LENGTH: + r = atou64(s, len, 10, &xar->file->length); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_DATA; - xar->file->length = atol10(s, len); break; case FILE_DATA_OFFSET: + r = atou64(s, len, 10, &xar->file->offset); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_DATA; - xar->file->offset = atol10(s, len); break; case FILE_DATA_SIZE: + r = atou64(s, len, 10, &xar->file->size); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_DATA; - xar->file->size = atol10(s, len); break; case FILE_DATA_A_CHECKSUM: xar->file->a_sum.len = atohex(xar->file->a_sum.val, @@ -2822,16 +2872,22 @@ xml_data(void *userData, const char *s, size_t len) sizeof(xar->file->e_sum.val), s, len); break; case FILE_EA_LENGTH: + r = atou64(s, len, 10, &xar->xattr->length); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_XATTR; - xar->xattr->length = atol10(s, len); break; case FILE_EA_OFFSET: + r = atou64(s, len, 10, &xar->xattr->offset); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_XATTR; - xar->xattr->offset = atol10(s, len); break; case FILE_EA_SIZE: + r = atou64(s, len, 10, &xar->xattr->size); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_XATTR; - xar->xattr->size = atol10(s, len); break; case FILE_EA_A_CHECKSUM: xar->file->has |= HAS_XATTR; @@ -2907,6 +2963,8 @@ xml_data(void *userData, const char *s, size_t len) case UNKNOWN: break; } + + return (ARCHIVE_OK); } /* @@ -3205,7 +3263,12 @@ xml2_read_toc(struct archive_read *a) break; case XML_READER_TYPE_TEXT: value = (const char *)xmlTextReaderConstValue(reader); - xml_data(a, value, strlen(value)); + r = xml_data(a, value, strlen(value)); + if (r != ARCHIVE_OK) { + xmlFreeTextReader(reader); + xmlCleanupParser(); + return (r); + } break; case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: default: @@ -3285,7 +3348,10 @@ expat_data_cb(void *userData, const XML_Char *s, int len) { struct expat_userData *ud = (struct expat_userData *)userData; - xml_data(ud->archive, s, (size_t)len); + if (ud->state != ARCHIVE_OK) + return; + + ud->state = xml_data(ud->archive, s, (size_t)len); } static int @@ -3321,7 +3387,7 @@ expat_read_toc(struct archive_read *a) d = NULL; r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining); - if (r != ARCHIVE_OK) { + if (r != ARCHIVE_OK || outbytes > INT_MAX) { XML_ParserFree(parser); return (r); } @@ -3638,7 +3704,11 @@ xmllite_read_toc(struct archive_read *a) goto out; } - xml_data(a, as.s, (int)archive_strlen(&as)); + r = xml_data(a, as.s, archive_strlen(&as)); + if (r != ARCHIVE_OK) { + /* xml_data sets an appropriate error */ + goto out; + } archive_string_free(&as); case XmlNodeType_None: diff --git a/libarchive-clib/c/archive_read_support_format_zip.c b/libarchive-clib/c/archive_read_support_format_zip.c index 00796b2..fa9995d 100644 --- a/libarchive-clib/c/archive_read_support_format_zip.c +++ b/libarchive-clib/c/archive_read_support_format_zip.c @@ -272,6 +272,10 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); #endif +static void +trad_enc_decrypt_update(struct trad_enc_ctx *, const uint8_t *, size_t, + uint8_t *, size_t); + /* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8 * streams inside ZIP files. It has 2 purposes: one is to fetch the next * compressed byte from the stream, second one is to increase the counter how @@ -281,15 +285,32 @@ ppmd_read(void* p) { /* Get the handle to current decompression context. */ struct archive_read *a = ((IByteIn*)p)->a; struct zip *zip = (struct zip*) a->format->data; - ssize_t bytes_avail = 0; /* Fetch next byte. */ - const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail); - if(bytes_avail < 1) { + const uint8_t* data = __archive_read_ahead(a, 1, NULL); + if(data == NULL) { zip->ppmd8_stream_failed = 1; return 0; } + if (zip->tctx_valid || zip->cctx_valid) { + uint8_t val; + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + data, 1, &val, 1); + } else { + size_t dsize = 1; + archive_decrypto_aes_ctr_update(&zip->cctx, + data, 1, &val, &dsize); + } + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, data, 1); + + __archive_read_consume(a, 1); + ++zip->zipx_ppmd_read_compressed; + return val; + } + __archive_read_consume(a, 1); /* Increment the counter. */ @@ -401,6 +422,146 @@ crypt_derive_key_sha1(const void *p, int size, unsigned char *key, } #endif +/* Read and decrypt bytes for zipx init headers. + * Used by format-specific init functions (lzma, ppmd) that need to + * read a small header from the compressed stream. When encryption is + * active the bytes are decrypted in-place into the decryption buffer. */ +static int +zipx_read_header_and_decrypt(struct archive_read *a, const void **buf, size_t in_len, + size_t *out_len, size_t *consumed) +{ + struct zip *zip = (struct zip *)(a->format->data); + const void *raw; + ssize_t bytes_avail; + size_t to_decrypt; + + raw = __archive_read_ahead(a, in_len, &bytes_avail); + if (raw == NULL || bytes_avail < (ssize_t)in_len) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); + } + + if (zip->tctx_valid || zip->cctx_valid) { + to_decrypt = in_len; + if (to_decrypt > zip->decrypted_buffer_size) + to_decrypt = zip->decrypted_buffer_size; + + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + raw, to_decrypt, + zip->decrypted_buffer, to_decrypt); + } else { + size_t dsize = to_decrypt; + archive_decrypto_aes_ctr_update(&zip->cctx, + raw, to_decrypt, + zip->decrypted_buffer, &dsize); + } + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, + raw, to_decrypt); + + *buf = zip->decrypted_buffer; + *out_len = to_decrypt; + *consumed = to_decrypt; + } else { + *buf = raw; + *out_len = in_len; + *consumed = in_len; + } + return (ARCHIVE_OK); +} + +/* Decrypt bulk compressed data for zipx decompression. + * Manages the decryption buffer, handles partial fills, and returns decrypted + * data pointer + length. `sp` is set to the raw pointer for HMAC accounting. */ +static void +zip_read_decrypt(struct zip *zip, const void *compressed_buff, + ssize_t bytes_avail, const void **result_buff, ssize_t *result_avail, + const void **sp) +{ + *sp = compressed_buff; + + if (zip->tctx_valid || zip->cctx_valid) { + if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { + size_t buff_remaining = + (zip->decrypted_buffer + + zip->decrypted_buffer_size) + - (zip->decrypted_ptr + + zip->decrypted_bytes_remaining); + /* The new bytes to decrypt start after decrypted_bytes_remaining + * in the raw stream: those leading bytes were already + * decrypted on a previous call but have not yet been consumed. */ + size_t new_bytes = + (size_t)bytes_avail + - zip->decrypted_bytes_remaining; + + if (buff_remaining > new_bytes) + buff_remaining = new_bytes; + + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && + zip->entry_bytes_remaining > 0) { + if ((int64_t)(zip->decrypted_bytes_remaining + + buff_remaining) + > zip->entry_bytes_remaining) { + if (zip->entry_bytes_remaining < + (int64_t)zip->decrypted_bytes_remaining) + buff_remaining = 0; + else + buff_remaining = + (size_t)zip->entry_bytes_remaining + - zip->decrypted_bytes_remaining; + } + } + if (buff_remaining > 0) { + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + (const uint8_t *)compressed_buff + + zip->decrypted_bytes_remaining, + buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + buff_remaining); + } else { + size_t dsize = buff_remaining; + archive_decrypto_aes_ctr_update( + &zip->cctx, + (const uint8_t *)compressed_buff + + zip->decrypted_bytes_remaining, + buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + &dsize); + } + zip->decrypted_bytes_remaining += + buff_remaining; + } + } + *result_avail = zip->decrypted_bytes_remaining; + *result_buff = (const char *)zip->decrypted_ptr; + } else { + *result_buff = compressed_buff; + *result_avail = bytes_avail; + } +} + +/* Post-decompression decrypt state update. + * Updates decrypt buffer pointers and HMAC after the decompressor + * has consumed `to_consume` bytes. */ +static void +zip_read_decrypt_update(struct zip *zip, ssize_t to_consume, const void *sp) +{ + if (zip->tctx_valid || zip->cctx_valid) { + zip->decrypted_bytes_remaining -= to_consume; + if (zip->decrypted_bytes_remaining == 0) + zip->decrypted_ptr = zip->decrypted_buffer; + else + zip->decrypted_ptr += to_consume; + } + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, sp, to_consume); +} + /* * Common code for streaming or seeking modes. * @@ -1775,7 +1936,7 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) * "lzma alone" decoder from XZ Utils. */ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); - r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX); + r = lzma_alone_decoder(&zip->zipx_lzma_stream, 576 * ((uint64_t)1 << 20)); if (r != LZMA_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "lzma initialization failed (%d)", r); @@ -1825,12 +1986,32 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) */ /* Read magic1,magic2,lzma_params from the ZIPX stream. */ - if(zip->entry_bytes_remaining < 9 || (p = __archive_read_ahead(a, 9, NULL)) == NULL) { + if(zip->entry_bytes_remaining < 9) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzma data"); return (ARCHIVE_FATAL); } + if (zip->tctx_valid || zip->cctx_valid) { + const void *decrypted; + size_t out_len; + size_t consumed; + int ret; + + ret = zipx_read_header_and_decrypt(a, &decrypted, 9, &out_len, &consumed); + if (ret != ARCHIVE_OK) + return ret; + p = decrypted; + } else { + p = __archive_read_ahead(a, 9, NULL); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma data"); + return (ARCHIVE_FATAL); + } + } + if(p[2] != 0x05 || p[3] != 0x00) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid lzma data"); @@ -1868,8 +2049,12 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) * output bytes yet. */ r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); if (r != LZMA_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "lzma stream initialization error"); + if (r == LZMA_MEMLIMIT_ERROR) + archive_set_error(&a->archive, ENOMEM, + "lzma stream requires too much memory"); + else + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "lzma stream initialization error"); return ARCHIVE_FATAL; } @@ -1890,6 +2075,7 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, int ret; lzma_ret lz_ret; const void* compressed_buf; + const void* sp; ssize_t bytes_avail, in_bytes, to_consume = 0; (void) offset; /* UNUSED */ @@ -1901,7 +2087,7 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, return (ret); } - compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + compressed_buf = sp = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated xz file body"); @@ -1909,6 +2095,10 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, } in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); + + zip_read_decrypt(zip, compressed_buf, in_bytes, + &compressed_buf, &in_bytes, &sp); + zip->zipx_lzma_stream.next_in = compressed_buf; zip->zipx_lzma_stream.avail_in = in_bytes; zip->zipx_lzma_stream.total_in = 0; @@ -1957,6 +2147,16 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + zip_read_decrypt_update(zip, to_consume, sp); + + if (zip->end_of_entry) { + if (zip->hctx_valid) { + ret = check_authentication_code(a, NULL); + if (ret != ARCHIVE_OK) + return ret; + } + } + *size = (size_t)zip->zipx_lzma_stream.total_out; *buff = zip->uncompressed_buffer; @@ -1971,6 +2171,7 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, int ret; lzma_ret lz_ret; const void* compressed_buf; + const void* sp; ssize_t bytes_avail, in_bytes, to_consume; (void) offset; /* UNUSED */ @@ -2000,6 +2201,9 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, /* Set decompressor parameters. */ in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); + zip_read_decrypt(zip, compressed_buf, in_bytes, + &compressed_buf, &in_bytes, &sp); + zip->zipx_lzma_stream.next_in = compressed_buf; zip->zipx_lzma_stream.avail_in = in_bytes; zip->zipx_lzma_stream.total_in = 0; @@ -2039,6 +2243,12 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, case LZMA_OK: break; + case LZMA_BUF_ERROR: + if (zip->zipx_lzma_stream.avail_out == 0) { + zip->end_of_entry = 1; + break; + } + /* FALL THROUGH */ default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "lzma unknown error (%d)", (int) lz_ret); @@ -2053,16 +2263,37 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + zip_read_decrypt_update(zip, to_consume, sp); + if(zip->entry_bytes_remaining == 0) { zip->end_of_entry = 1; } + if(zip->end_of_entry && zip->entry_bytes_remaining > 0) { + ssize_t remaining = (ssize_t)zip->entry_bytes_remaining; + const void *p = __archive_read_ahead(a, remaining, NULL); + if (p != NULL) { + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, + p, remaining); + __archive_read_consume(a, remaining); + zip->entry_compressed_bytes_read += remaining; + zip->entry_bytes_remaining = 0; + } + } + /* Free lzma decoder handle because we'll no longer need it. */ /* This cannot be folded into LZMA_STREAM_END handling above * because the stream end marker is not required in this format. */ if(zip->end_of_entry) { lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; + + if (zip->hctx_valid) { + ret = check_authentication_code(a, NULL); + if (ret != ARCHIVE_OK) + return ret; + } } /* Return values. */ @@ -2104,11 +2335,22 @@ zipx_ppmd8_init(struct archive_read *a, struct zip *zip) zip->zipx_ppmd_read_compressed = 0; /* Read Ppmd8 header (2 bytes). */ - p = __archive_read_ahead(a, 2, NULL); - if(!p) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated file data in PPMd8 stream"); - return (ARCHIVE_FATAL); + if (zip->tctx_valid || zip->cctx_valid) { + size_t out_len; + size_t consumed; + int ret; + + ret = zipx_read_header_and_decrypt(a, &p, 2, &out_len, &consumed); + if (ret != ARCHIVE_OK) + return ret; + } else { + p = __archive_read_ahead(a, 2, NULL); + if(!p) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated file data in PPMd8 stream"); + return (ARCHIVE_FATAL); + } } __archive_read_consume(a, 2); @@ -2178,7 +2420,6 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, struct zip* zip = (struct zip *)(a->format->data); int ret; size_t consumed_bytes = 0; - ssize_t bytes_avail = 0; (void) offset; /* UNUSED */ @@ -2192,8 +2433,7 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, /* Fetch for more data. We're reading 1 byte here, but libarchive * should prefetch more bytes. */ - (void) __archive_read_ahead(a, 1, &bytes_avail); - if(bytes_avail < 0) { + if(__archive_read_ahead(a, 1, NULL) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated PPMd8 file body"); return (ARCHIVE_FATAL); @@ -2234,6 +2474,12 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, if(zip->end_of_entry) { __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); zip->ppmd8_valid = 0; + + if (zip->hctx_valid) { + int r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return (r); + } } /* Update pointers for libarchive. */ @@ -2293,6 +2539,7 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, struct zip *zip = (struct zip *)(a->format->data); ssize_t bytes_avail = 0, in_bytes, to_consume; const void *compressed_buff; + const void *sp; int r; uint64_t total_out; @@ -2325,6 +2572,9 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } + zip_read_decrypt(zip, compressed_buff, in_bytes, + &compressed_buff, &in_bytes, &sp); + /* Setup buffer boundaries. */ zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; zip->bzstream.avail_in = (uint32_t)in_bytes; @@ -2375,6 +2625,14 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += total_out; + zip_read_decrypt_update(zip, to_consume, sp); + + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return r; + } + /* Give libarchive its due. */ *size = (size_t)total_out; *buff = zip->uncompressed_buffer; @@ -2436,6 +2694,7 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, struct zip *zip = (struct zip *)(a->format->data); ssize_t bytes_avail = 0, in_bytes, to_consume; const void *compressed_buff; + const void *sp; int r; size_t ret; uint64_t total_out; @@ -2452,7 +2711,7 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, } /* Fetch more compressed bytes */ - compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); + compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail); if(bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated zstd file body"); @@ -2470,6 +2729,9 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } + zip_read_decrypt(zip, compressed_buff, in_bytes, + &compressed_buff, &in_bytes, &sp); + /* Setup buffer boundaries */ in.src = compressed_buff; in.size = in_bytes; @@ -2504,6 +2766,14 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += total_out; + zip_read_decrypt_update(zip, to_consume, sp); + + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return r; + } + /* Give libarchive its due. */ *size = (size_t)total_out; *buff = zip->uncompressed_buffer; @@ -2544,7 +2814,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, { struct zip *zip; ssize_t bytes_avail, to_consume = 0; - const void *compressed_buff, *sp; + const void *compressed_buff; + const void *sp; int r; (void)offset; /* UNUSED */ @@ -2584,54 +2855,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } - if (zip->tctx_valid || zip->cctx_valid) { - if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { - size_t buff_remaining = - (zip->decrypted_buffer + - zip->decrypted_buffer_size) - - (zip->decrypted_ptr + - zip->decrypted_bytes_remaining); - - if (buff_remaining > (size_t)bytes_avail) - buff_remaining = (size_t)bytes_avail; - - if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && - zip->entry_bytes_remaining > 0) { - if ((int64_t)(zip->decrypted_bytes_remaining - + buff_remaining) - > zip->entry_bytes_remaining) { - if (zip->entry_bytes_remaining < - (int64_t)zip->decrypted_bytes_remaining) - buff_remaining = 0; - else - buff_remaining = - (size_t)zip->entry_bytes_remaining - - zip->decrypted_bytes_remaining; - } - } - if (buff_remaining > 0) { - if (zip->tctx_valid) { - trad_enc_decrypt_update(&zip->tctx, - compressed_buff, buff_remaining, - zip->decrypted_ptr - + zip->decrypted_bytes_remaining, - buff_remaining); - } else { - size_t dsize = buff_remaining; - archive_decrypto_aes_ctr_update( - &zip->cctx, - compressed_buff, buff_remaining, - zip->decrypted_ptr - + zip->decrypted_bytes_remaining, - &dsize); - } - zip->decrypted_bytes_remaining += - buff_remaining; - } - } - bytes_avail = zip->decrypted_bytes_remaining; - compressed_buff = (const char *)zip->decrypted_ptr; - } + zip_read_decrypt(zip, compressed_buff, bytes_avail, + &compressed_buff, &bytes_avail, &sp); /* * A bug in zlib.h: stream.next_in should be marked 'const' @@ -2670,22 +2895,12 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->stream.total_out; - if (zip->tctx_valid || zip->cctx_valid) { - zip->decrypted_bytes_remaining -= to_consume; - if (zip->decrypted_bytes_remaining == 0) - zip->decrypted_ptr = zip->decrypted_buffer; - else - zip->decrypted_ptr += to_consume; - } - if (zip->hctx_valid) - archive_hmac_sha1_update(&zip->hctx, sp, to_consume); + zip_read_decrypt_update(zip, to_consume, sp); - if (zip->end_of_entry) { - if (zip->hctx_valid) { - r = check_authentication_code(a, NULL); - if (r != ARCHIVE_OK) { - return (r); - } + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) { + return r; } } diff --git a/libarchive-clib/c/archive_string.c b/libarchive-clib/c/archive_string.c index c6ae896..bbcec44 100644 --- a/libarchive-clib/c/archive_string.c +++ b/libarchive-clib/c/archive_string.c @@ -1314,7 +1314,17 @@ create_sconv_object(const char *fc, const char *tc, else if (strcmp(fc, "CP932") == 0) sc->cd = iconv_open(tc, "SJIS"); } -#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(__FreeBSD__) && !defined(HAVE_LIBICONV) + /* + * FreeBSD's native iconv() by default returns the number of + * invalid characters in the input string, as specified by + * POSIX, but iconv_strncat_in_locale() assumes GNU iconv + * semantics. + */ + int v = 1; + + (void)iconvctl(sc->cd, ICONV_SET_ILSEQ_INVALID, &v); +#elif defined(_WIN32) && !defined(__CYGWIN__) /* * archive_mstring on Windows directly convert multi-bytes * into archive_wstring in order not to depend on locale @@ -1362,7 +1372,7 @@ free_sconv_object(struct archive_string_conv *sc) } #if defined(_WIN32) && !defined(__CYGWIN__) -# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # define GetOEMCP() CP_OEMCP # endif diff --git a/libarchive-clib/c/archive_time.c b/libarchive-clib/c/archive_time.c index 3352c80..bc77082 100644 --- a/libarchive-clib/c/archive_time.c +++ b/libarchive-clib/c/archive_time.c @@ -24,11 +24,20 @@ */ #include "archive_platform.h" -#include "archive_private.h" -#include "archive_time_private.h" + +#ifdef HAVE_LIMITS_H #include +#endif +#ifdef HAVE_STDLIB_H #include +#endif +#ifdef HAVE_STRING_H #include +#endif + +#include "archive_integer.h" +#include "archive_private.h" +#include "archive_time_private.h" #define NTFS_EPOC_TIME ARCHIVE_LITERAL_ULL(11644473600) #define NTFS_TICKS ARCHIVE_LITERAL_ULL(10000000) @@ -149,15 +158,11 @@ unix_to_ntfs(int64_t secs, uint32_t nsecs) if (secs < -(int64_t)NTFS_EPOC_TIME) return 0; - ntfs = secs + NTFS_EPOC_TIME; - - if (ntfs > UINT64_MAX / NTFS_TICKS) - return UINT64_MAX; - - ntfs *= NTFS_TICKS; - - if (ntfs > UINT64_MAX - nsecs/100) + /* (secs + NTFS_EPOC_TIME) * NTFS_TICKS + nsecs / 100 */ + if (archive_ckd_add_u64(&ntfs, secs, NTFS_EPOC_TIME) || + archive_ckd_mul_u64(&ntfs, ntfs, NTFS_TICKS) || + archive_ckd_add_u64(&ntfs, ntfs, nsecs / 100)) return UINT64_MAX; - return ntfs + nsecs/100; + return ntfs; } diff --git a/libarchive-clib/c/archive_util.c b/libarchive-clib/c/archive_util.c index 0d1de1e..5e0b916 100644 --- a/libarchive-clib/c/archive_util.c +++ b/libarchive-clib/c/archive_util.c @@ -42,18 +42,12 @@ #include #endif #if defined(_WIN32) && !defined(__CYGWIN__) -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA -/* don't use bcrypt when XP needs to be supported */ #include /* Common in other bcrypt implementations, but missing from VS2008. */ #ifndef BCRYPT_SUCCESS #define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) #endif - -#elif defined(HAVE_WINCRYPT_H) -#include -#endif #endif #ifdef HAVE_ZLIB_H #include @@ -110,8 +104,7 @@ archive_errno(struct archive *a) const char * archive_error_string(struct archive *a) { - - if (a->error != NULL && *a->error != '\0') + if (a->error != NULL && *a->error != '\0') return (a->error); else return (NULL); @@ -250,11 +243,7 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) DWORD attr; wchar_t *xp, *ep; int fd; -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA BCRYPT_ALG_HANDLE hAlg = NULL; -#else - HCRYPTPROV hProv = (HCRYPTPROV)NULL; -#endif fd = -1; ws = NULL; archive_string_init(&temp_name); @@ -262,22 +251,36 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) if (template == NULL) { /* Get a temporary directory. */ if (tmpdir == NULL) { - size_t l; - wchar_t *tmp; + wchar_t buf[MAX_PATH + 1]; + wchar_t *p = buf, *buf2 = NULL; + size_t l, s; - l = GetTempPathW(0, NULL); + s = MAX_PATH + 1; + l = GetTempPathW((DWORD)s, buf); if (l == 0) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } - tmp = malloc(l*sizeof(wchar_t)); - if (tmp == NULL) { - errno = ENOMEM; - goto exit_tmpfile; + while (l > s) { + wchar_t *tmp; + + s = l; + tmp = realloc(buf2, s * sizeof(wchar_t)); + if (tmp == NULL) { + free(buf2); + errno = ENOMEM; + goto exit_tmpfile; + } + p = buf2 = tmp; + l = GetTempPathW((DWORD)s, buf2); + if (l == 0) { + free(buf2); + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } } - GetTempPathW((DWORD)l, tmp); - archive_wstrcpy(&temp_name, tmp); - free(tmp); + archive_wstrcpy(&temp_name, p); + free(buf2); } else { if (archive_wstring_append_from_mbs(&temp_name, tmpdir, strlen(tmpdir)) < 0) @@ -328,19 +331,11 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) abort(); } -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0))) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } -#else - if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - la_dosmaperr(GetLastError()); - goto exit_tmpfile; - } -#endif for (;;) { wchar_t *p; @@ -351,19 +346,11 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) /* Generate a random file name through CryptGenRandom(). */ p = xp; -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p, (DWORD)(ep - p)*sizeof(wchar_t), 0))) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } -#else - if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), - (BYTE*)p)) { - la_dosmaperr(GetLastError()); - goto exit_tmpfile; - } -#endif for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; @@ -417,13 +404,8 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) break;/* success! */ } exit_tmpfile: -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA if (hAlg != NULL) BCryptCloseAlgorithmProvider(hAlg, 0); -#else - if (hProv != (HCRYPTPROV)NULL) - CryptReleaseContext(hProv, 0); -#endif free(ws); if (template == temp_name.s) archive_wstring_free(&temp_name); diff --git a/libarchive-clib/c/archive_version_details.c b/libarchive-clib/c/archive_version_details.c index 9063faa..be11aad 100644 --- a/libarchive-clib/c/archive_version_details.c +++ b/libarchive-clib/c/archive_version_details.c @@ -157,10 +157,6 @@ archive_crypto_version(struct archive_string* str) #if defined(ARCHIVE_CRYPTOR_USE_LIBMD) archive_strcat(str, " libmd/"); archive_strcat(str, archive_libmd_version()); -#endif -#if defined(ARCHIVE_CRYPTOR_USE_WINCRYPT) - archive_strcat(str, " WinCrypt/"); - archive_strcat(str, archive_wincrypt_version()); #endif // Just in case (void)str; /* UNUSED */ @@ -180,7 +176,7 @@ archive_version_details(void) const char *libiconv = archive_libiconv_version(); const char *libacl = archive_libacl_version(); const char *librichacl = archive_librichacl_version(); - const char *libattr = archive_libacl_version(); + const char *libattr = archive_libattr_version(); if (!init) { archive_string_init(&str); @@ -234,6 +230,7 @@ archive_version_details(void) archive_strcat(&str, " libiconv/"); archive_strcat(&str, libiconv); } + init = 1; } return str.s; } @@ -431,27 +428,7 @@ archive_cng_version(void) const char * archive_wincrypt_version(void) { -#if defined(ARCHIVE_CRYPTOR_USE_WINCRYPT) || defined(ARCHIVE_CRYPTO_WINCRYPT) - HCRYPTPROV prov; - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - if (GetLastError() != (DWORD)NTE_BAD_KEYSET) - return NULL; - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) - return NULL; - } - DWORD version, length = sizeof(version); - if (!CryptGetProvParam(prov, PP_VERSION, (BYTE *)&version, &length, 0)) { - return NULL; - } else { - char major = (version >> 8) & 0xFF; - char minor = version & 0xFF; - static char wincrypt_version[6]; - snprintf(wincrypt_version, 6, "%hhd.%hhd", major, minor); - return wincrypt_version; - } -#else return NULL; -#endif } const char * diff --git a/libarchive-clib/c/archive_windows.c b/libarchive-clib/c/archive_windows.c index 3fbea6c..b787a0d 100644 --- a/libarchive-clib/c/archive_windows.c +++ b/libarchive-clib/c/archive_windows.c @@ -63,23 +63,6 @@ #include #include -#if defined(__LA_LSEEK_NEEDED) -static BOOL SetFilePointerEx_perso(HANDLE hFile, - LARGE_INTEGER liDistanceToMove, - PLARGE_INTEGER lpNewFilePointer, - DWORD dwMoveMethod) -{ - LARGE_INTEGER li; - li.QuadPart = liDistanceToMove.QuadPart; - li.LowPart = SetFilePointer( - hFile, li.LowPart, &li.HighPart, dwMoveMethod); - if(lpNewFilePointer) { - lpNewFilePointer->QuadPart = li.QuadPart; - } - return li.LowPart != -1 || GetLastError() == NO_ERROR; -} -#endif - struct ustat { int64_t st_atime; uint32_t st_atime_nsec; @@ -236,7 +219,7 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, CREATEFILE2_EXTENDED_PARAMETERS createExParams; #endif -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) handle = CreateFileA(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); @@ -285,7 +268,7 @@ __la_lseek(int fd, __int64 offset, int whence) return (-1); } distance.QuadPart = offset; - if (!SetFilePointerEx_perso(handle, distance, &newpointer, whence)) { + if (!SetFilePointerEx(handle, distance, &newpointer, whence)) { DWORD lasterr; lasterr = GetLastError(); @@ -324,7 +307,7 @@ __la_open(const char *path, int flags, ...) * "Permission denied" error. */ attr = GetFileAttributesA(path); -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) #endif { @@ -342,7 +325,7 @@ __la_open(const char *path, int flags, ...) } if (attr & FILE_ATTRIBUTE_DIRECTORY) { HANDLE handle; -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (ws != NULL) handle = CreateFileW(ws, 0, 0, NULL, OPEN_EXISTING, @@ -437,7 +420,7 @@ __la_wopen(const wchar_t *path, int flags, ...) * "Permission denied" error. */ attr = GetFileAttributesW(path); -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) #endif { @@ -456,7 +439,7 @@ __la_wopen(const wchar_t *path, int flags, ...) } if (attr & FILE_ATTRIBUTE_DIRECTORY) { HANDLE handle; -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (fullpath != NULL) handle = CreateFileW(fullpath, 0, 0, NULL, OPEN_EXISTING, diff --git a/libarchive-clib/c/archive_windows.h b/libarchive-clib/c/archive_windows.h index ecb14e0..fef7cf9 100644 --- a/libarchive-clib/c/archive_windows.h +++ b/libarchive-clib/c/archive_windows.h @@ -299,6 +299,26 @@ typedef int mbstate_t; size_t wcrtomb(char *, wchar_t, mbstate_t *); #endif +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_FAMILY_PARTITION(x) (x) +#endif + +#ifndef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef WINAPI_PARTITION_SYSTEM +#define WINAPI_PARTITION_SYSTEM 1 +#endif + +#ifndef NTDDI_VERSION +#define NTDDI_VERSION 0x05020000 +#endif + +#ifndef NTDDI_WIN10_VB +#define NTDDI_WIN10_VB 0x0A000008 +#endif + #if !WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) && NTDDI_VERSION < NTDDI_WIN10_VB // not supported in UWP SDK before 20H1 #define GetVolumePathNameW(f, v, c) (0) @@ -309,17 +329,4 @@ WINBASEAPI BOOL WINAPI GetVolumePathNameW( DWORD cchBufferLength ); #endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -# if _WIN32_WINNT < 0x0500 /* windows.h not providing 0x500 API */ -typedef struct _FILE_ALLOCATED_RANGE_BUFFER { - LARGE_INTEGER FileOffset; - LARGE_INTEGER Length; -} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; -# define FSCTL_SET_SPARSE \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_WRITE_DATA) -# define FSCTL_QUERY_ALLOCATED_RANGES \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_DATA) -# endif -#endif - #endif /* !LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */ diff --git a/libarchive-clib/c/archive_write.c b/libarchive-clib/c/archive_write.c index 14dc733..68f452e 100644 --- a/libarchive-clib/c/archive_write.c +++ b/libarchive-clib/c/archive_write.c @@ -761,7 +761,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) archive_entry_ino_is_set(entry) && archive_entry_dev(entry) == (dev_t)a->skip_file_dev && archive_entry_ino64(entry) == a->skip_file_ino) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Can't add archive to itself"); return (ARCHIVE_FAILED); } diff --git a/libarchive-clib/c/archive_write_add_filter.c b/libarchive-clib/c/archive_write_add_filter.c index aa96251..ddcf282 100644 --- a/libarchive-clib/c/archive_write_add_filter.c +++ b/libarchive-clib/c/archive_write_add_filter.c @@ -49,7 +49,7 @@ struct { int code; int (*setter)(struct archive *); } codes[] = { ARCHIVE_FILTER_LZ4, archive_write_add_filter_lz4 }, { ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip }, { ARCHIVE_FILTER_LZMA, archive_write_add_filter_lzma }, - { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip }, + { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzop }, { ARCHIVE_FILTER_UU, archive_write_add_filter_uuencode }, { ARCHIVE_FILTER_XZ, archive_write_add_filter_xz }, { ARCHIVE_FILTER_ZSTD, archive_write_add_filter_zstd }, diff --git a/libarchive-clib/c/archive_write_add_filter_b64encode.c b/libarchive-clib/c/archive_write_add_filter_b64encode.c index d2c4742..2c4e5ba 100644 --- a/libarchive-clib/c/archive_write_add_filter_b64encode.c +++ b/libarchive-clib/c/archive_write_add_filter_b64encode.c @@ -39,6 +39,7 @@ #endif #include "archive.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" @@ -308,10 +309,9 @@ atol8(const char *p, size_t char_cnt) else return (-1); p++; - if (l > (INT64_MAX >> 3)) + if (archive_ckd_mul_i64(&l, l, 8) || + archive_ckd_add_i64(&l, l, digit)) return (-1); - l <<= 3; - l |= digit; } return (l); } diff --git a/libarchive-clib/c/archive_write_add_filter_bzip2.c b/libarchive-clib/c/archive_write_add_filter_bzip2.c index 94b342d..209b152 100644 --- a/libarchive-clib/c/archive_write_add_filter_bzip2.c +++ b/libarchive-clib/c/archive_write_add_filter_bzip2.c @@ -57,7 +57,6 @@ struct private_data { int compression_level; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream stream; - int64_t total_in; char *compressed; size_t compressed_buffer_size; #else @@ -239,9 +238,6 @@ archive_compressor_bzip2_write(struct archive_write_filter *f, { struct private_data *data = (struct private_data *)f->data; - /* Update statistics */ - data->total_in += length; - /* Compress input data to output buffer */ SET_NEXT_IN(data, buff); data->stream.avail_in = (uint32_t)length; diff --git a/libarchive-clib/c/archive_write_add_filter_gzip.c b/libarchive-clib/c/archive_write_add_filter_gzip.c index 0a2f224..7112ef3 100644 --- a/libarchive-clib/c/archive_write_add_filter_gzip.c +++ b/libarchive-clib/c/archive_write_add_filter_gzip.c @@ -40,6 +40,7 @@ #endif #include "archive.h" +#include "archive_endian.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" @@ -61,7 +62,7 @@ struct private_data { char *original_filename; #ifdef HAVE_ZLIB_H z_stream stream; - int64_t total_in; + uint64_t total_in; unsigned char *compressed; size_t compressed_buffer_size; unsigned long crc; @@ -178,7 +179,7 @@ archive_compressor_gzip_options(struct archive_write_filter *f, const char *key, if (value) { data->original_filename = strdup(value); if (data->original_filename == NULL) - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } @@ -230,11 +231,8 @@ archive_compressor_gzip_open(struct archive_write_filter *f) data->compressed[2] = 0x08; /* "Deflate" compression */ data->compressed[3] = 0x00; /* Flags */ if (data->timestamp >= 0) { - time_t t = time(NULL); - data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */ - data->compressed[5] = (uint8_t)(t>>8)&0xff; - data->compressed[6] = (uint8_t)(t>>16)&0xff; - data->compressed[7] = (uint8_t)(t>>24)&0xff; + uint32_t t = (uint32_t)time(NULL); + archive_le32enc(data->compressed + 4, t); /* Timestamp */ } else { memset(&data->compressed[4], 0, 4); } @@ -253,9 +251,9 @@ archive_compressor_gzip_open(struct archive_write_filter *f) /* Limit "original filename" to 32k or the * remaining space in the buffer, whichever is smaller. */ - int ofn_length = strlen(data->original_filename); - int ofn_max_length = 32768; - int ofn_space_available = data->compressed + size_t ofn_length = strlen(data->original_filename); + size_t ofn_max_length = 32768; + size_t ofn_space_available = data->compressed + data->compressed_buffer_size - data->stream.next_out - 1; @@ -358,14 +356,8 @@ archive_compressor_gzip_close(struct archive_write_filter *f) } if (ret == ARCHIVE_OK) { /* Build and write out 8-byte trailer. */ - trailer[0] = (uint8_t)(data->crc)&0xff; - trailer[1] = (uint8_t)(data->crc >> 8)&0xff; - trailer[2] = (uint8_t)(data->crc >> 16)&0xff; - trailer[3] = (uint8_t)(data->crc >> 24)&0xff; - trailer[4] = (uint8_t)(data->total_in)&0xff; - trailer[5] = (uint8_t)(data->total_in >> 8)&0xff; - trailer[6] = (uint8_t)(data->total_in >> 16)&0xff; - trailer[7] = (uint8_t)(data->total_in >> 24)&0xff; + archive_le32enc(trailer, data->crc); + archive_le32enc(trailer + 4, data->total_in); ret = __archive_write_filter(f->next_filter, trailer, 8); } diff --git a/libarchive-clib/c/archive_write_add_filter_lz4.c b/libarchive-clib/c/archive_write_add_filter_lz4.c index efc408e..de5283f 100644 --- a/libarchive-clib/c/archive_write_add_filter_lz4.c +++ b/libarchive-clib/c/archive_write_add_filter_lz4.c @@ -61,7 +61,6 @@ struct private_data { unsigned preset_dictionary:1; unsigned block_maximum_size:3; #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 - int64_t total_in; char *out; char *out_buffer; size_t out_buffer_size; @@ -88,7 +87,7 @@ static int archive_filter_lz4_write(struct archive_write_filter *, const void *, size_t); /* - * Add a lz4 compression filter to this write handle. + * Add an lz4 compression filter to this write handle. */ int archive_write_add_filter_lz4(struct archive *_a) @@ -228,7 +227,7 @@ archive_filter_lz4_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; size_t required_size; - static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, + static const size_t bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 4 * 1024 * 1024 }; size_t pre_block_size; @@ -243,7 +242,7 @@ archive_filter_lz4_open(struct archive_write_filter *f) free(data->out_buffer); if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of - * the of bytes per block for performance. */ + * the bytes per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) bs = bpb; @@ -306,9 +305,6 @@ archive_filter_lz4_write(struct archive_write_filter *f, data->header_written = 1; } - /* Update statistics */ - data->total_in += length; - p = (const char *)buff; remaining = length; while (remaining) { @@ -417,9 +413,11 @@ lz4_write_stream_descriptor(struct archive_write_filter *f) sd[5] = (data->block_maximum_size << 4); sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff; data->out += 7; - if (data->stream_checksum) + if (data->stream_checksum) { data->xxh32_state = __archive_xxhash.XXH32_init(0); - else + if (data->xxh32_state == NULL) + return (ARCHIVE_FATAL); + } else data->xxh32_state = NULL; return (ARCHIVE_OK); } diff --git a/libarchive-clib/c/archive_write_add_filter_program.c b/libarchive-clib/c/archive_write_add_filter_program.c index f12db33..cb92dc0 100644 --- a/libarchive-clib/c/archive_write_add_filter_program.c +++ b/libarchive-clib/c/archive_write_add_filter_program.c @@ -184,6 +184,10 @@ __archive_write_program_allocate(const char *program) data->child_stdin = -1; data->child_stdout = -1; data->program_name = strdup(program); + if (data->program_name == NULL) { + free(data); + return (NULL); + } return (data); } diff --git a/libarchive-clib/c/archive_write_add_filter_uuencode.c b/libarchive-clib/c/archive_write_add_filter_uuencode.c index d25236f..b338b4a 100644 --- a/libarchive-clib/c/archive_write_add_filter_uuencode.c +++ b/libarchive-clib/c/archive_write_add_filter_uuencode.c @@ -39,6 +39,7 @@ #endif #include "archive.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" @@ -299,10 +300,9 @@ atol8(const char *p, size_t char_cnt) else return (-1); p++; - if (l > (INT64_MAX >> 3)) + if (archive_ckd_mul_i64(&l, l, 8) || + archive_ckd_add_i64(&l, l, digit)) return (-1); - l <<= 3; - l |= digit; } return (l); } diff --git a/libarchive-clib/c/archive_write_add_filter_zstd.c b/libarchive-clib/c/archive_write_add_filter_zstd.c index 7149abb..2c6b91e 100644 --- a/libarchive-clib/c/archive_write_add_filter_zstd.c +++ b/libarchive-clib/c/archive_write_add_filter_zstd.c @@ -73,7 +73,6 @@ struct private_data { size_t cur_frame; size_t cur_frame_in; size_t cur_frame_out; - size_t total_in; ZSTD_CStream *cstream; ZSTD_outBuffer out; #else @@ -505,7 +504,6 @@ drive_compressor(struct archive_write_filter *f, data->state = running; break; } - data->total_in += in.pos - ipos; data->cur_frame_in += in.pos - ipos; data->cur_frame_out += data->out.pos - opos; if (data->state == running) { diff --git a/libarchive-clib/c/archive_write_disk_posix.c b/libarchive-clib/c/archive_write_disk_posix.c index 7b90fc2..df46a5c 100644 --- a/libarchive-clib/c/archive_write_disk_posix.c +++ b/libarchive-clib/c/archive_write_disk_posix.c @@ -308,6 +308,10 @@ struct archive_write_disk { int stream_valid; int decmpfs_compression_level; #endif +#if !(ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX ||\ + ARCHIVE_XATTR_FREEBSD) + int warning_done; +#endif }; /* @@ -596,11 +600,12 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); a->entry = archive_entry_clone(entry); + if (a->entry == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } a->fd = -1; a->fd_offset = 0; a->offset = 0; @@ -971,7 +976,7 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } @@ -1595,7 +1600,7 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff, return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } @@ -1674,7 +1679,7 @@ _archive_write_disk_data_block(struct archive *_a, if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EOVERFLOW, "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize); return (ARCHIVE_WARN); @@ -2196,7 +2201,7 @@ restore_entry(struct archive_write_disk *a) if (a->skip_file_set && a->st.st_dev == (dev_t)a->skip_file_dev && a->st.st_ino == (ino_t)a->skip_file_ino) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } @@ -2996,7 +3001,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, */ /* if (!S_ISLNK(path)) { - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, -1, "Removing symlink ", path); } */ @@ -3012,7 +3017,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, #endif if (r != 0) { tail[0] = c; - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, EIO, "Cannot remove intervening " "symlink ", path); res = ARCHIVE_FAILED; @@ -3072,7 +3077,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, head = tail + 1; } else { tail[0] = c; - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, ELOOP, "Cannot extract through " "symlink ", path); res = ARCHIVE_FAILED; @@ -3080,7 +3085,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, } } else { tail[0] = c; - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, ELOOP, "Cannot extract through symlink ", path); res = ARCHIVE_FAILED; break; @@ -4693,12 +4698,10 @@ set_xattrs(struct archive_write_disk *a) static int set_xattrs(struct archive_write_disk *a) { - static int warning_done = 0; - /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ - if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { - warning_done = 1; + if (archive_entry_xattr_count(a->entry) != 0 && !a->warning_done) { + a->warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); diff --git a/libarchive-clib/c/archive_write_disk_windows.c b/libarchive-clib/c/archive_write_disk_windows.c index 8a970de..435f0cd 100644 --- a/libarchive-clib/c/archive_write_disk_windows.c +++ b/libarchive-clib/c/archive_write_disk_windows.c @@ -70,21 +70,6 @@ #define IO_REPARSE_TAG_SYMLINK 0xA000000CL #endif -static BOOL SetFilePointerEx_perso(HANDLE hFile, - LARGE_INTEGER liDistanceToMove, - PLARGE_INTEGER lpNewFilePointer, - DWORD dwMoveMethod) -{ - LARGE_INTEGER li; - li.QuadPart = liDistanceToMove.QuadPart; - li.LowPart = SetFilePointer( - hFile, li.LowPart, &li.HighPart, dwMoveMethod); - if(lpNewFilePointer) { - lpNewFilePointer->QuadPart = li.QuadPart; - } - return li.LowPart != (DWORD)-1 || GetLastError() == NO_ERROR; -} - struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; @@ -595,45 +580,11 @@ la_mktemp(struct archive_write_disk *a) return (fd); } -#if _WIN32_WINNT < _WIN32_WINNT_VISTA -static void * -la_GetFunctionKernel32(const char *name) -{ - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; - lib = LoadLibrary(TEXT("kernel32.dll")); - } - if (lib == NULL) { - fprintf(stderr, "Can't load kernel32.dll?!\n"); - exit(1); - } - return (void *)GetProcAddress(lib, name); -} -#endif - static int la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) { - static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); BOOL ret; - -#if _WIN32_WINNT < _WIN32_WINNT_XP - static int set; -/* CreateHardLinkW is available since XP and always loaded */ - if (!set) { - set = 1; - f = la_GetFunctionKernel32("CreateHardLinkW"); - } -#else - f = CreateHardLinkW; -#endif - if (!f) { - errno = ENOTSUP; - return (0); - } - ret = (*f)(linkname, target, NULL); + ret = CreateHardLinkW(linkname, target, NULL); if (!ret) { /* Under windows 2000, it is necessary to remove * the "\\?\" prefix. */ @@ -652,7 +603,7 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) target += 4; } #undef IS_UNC - ret = (*f)(linkname, target, NULL); + ret = CreateHardLinkW(linkname, target, NULL); } return (ret); } @@ -666,30 +617,18 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) static int la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, int linktype) { - static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); + BOOL ret = 0; +#if _WIN32_WINNT < _WIN32_WINNT_VISTA ||\ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + (void)linkname; /* UNUSED */ + (void)target; /* UNUSED */ + (void)linktype; /* UNUSED */ +#else wchar_t *ttarget, *p; size_t len; DWORD attrs = 0; DWORD flags = 0; DWORD newflags = 0; - BOOL ret = 0; - -#if _WIN32_WINNT < _WIN32_WINNT_VISTA -/* CreateSymbolicLinkW is available since Vista and always loaded */ - static int set; - if (!set) { - set = 1; - f = la_GetFunctionKernel32("CreateSymbolicLinkW"); - } -#else -# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - f = CreateSymbolicLinkW; -# else - f = NULL; -# endif -#endif - if (!f) - return (0); len = wcslen(target); if (len == 0) { @@ -751,15 +690,16 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, disk_unlink(linkname); } - ret = (*f)(linkname, ttarget, newflags); + ret = CreateSymbolicLinkW(linkname, ttarget, newflags); /* * Prior to Windows 10 calling CreateSymbolicLinkW() will fail * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set */ if (!ret) { - ret = (*f)(linkname, ttarget, flags); + ret = CreateSymbolicLinkW(linkname, ttarget, flags); } free(ttarget); +#endif return (ret); } @@ -773,7 +713,7 @@ la_ftruncate(HANDLE handle, int64_t length) return (-1); } distance.QuadPart = length; - if (!SetFilePointerEx_perso(handle, distance, NULL, FILE_BEGIN)) { + if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN)) { la_dosmaperr(GetLastError()); return (-1); } @@ -874,8 +814,11 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->current_fixup = NULL; a->deferred = 0; archive_entry_free(a->entry); - a->entry = NULL; a->entry = archive_entry_clone(entry); + if (a->entry == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } a->fh = INVALID_HANDLE_VALUE; a->fd_offset = 0; a->offset = 0; @@ -1106,7 +1049,7 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) return (ARCHIVE_OK); if (a->filesize == 0 || a->fh == INVALID_HANDLE_VALUE) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } @@ -1190,7 +1133,7 @@ _archive_write_disk_data_block(struct archive *_a, if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EOVERFLOW, "Write request too large"); return (ARCHIVE_WARN); } @@ -1607,7 +1550,7 @@ restore_entry(struct archive_write_disk *a) if (a->skip_file_set && bhfi_dev(&a->st) == a->skip_file_dev && bhfi_ino(&a->st) == a->skip_file_ino) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } @@ -2198,7 +2141,7 @@ check_symlinks(struct archive_write_disk *a) * symlink with another symlink. */ if (!S_ISLNK(a->mode)) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, -1, "Removing symlink %ls", a->name); } @@ -2218,7 +2161,7 @@ check_symlinks(struct archive_write_disk *a) r = disk_unlink(a->name); } if (r != 0) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Cannot remove intervening " "symlink %ls", a->name); pn[0] = c; @@ -2226,7 +2169,7 @@ check_symlinks(struct archive_write_disk *a) } a->pst = NULL; } else { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, ELOOP, "Cannot extract through symlink %ls", a->name); pn[0] = c; diff --git a/libarchive-clib/c/archive_write_set_format_7zip.c b/libarchive-clib/c/archive_write_set_format_7zip.c index 1bbd24d..74da63a 100644 --- a/libarchive-clib/c/archive_write_set_format_7zip.c +++ b/libarchive-clib/c/archive_write_set_format_7zip.c @@ -314,7 +314,7 @@ static int _7z_compression_init_encoder(struct archive_write *, unsigned, int); static int compression_init_encoder_zstd(struct archive *, struct la_zstream *, int, int); -#if defined(HAVE_ZSTD_H) +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream static int compression_code_zstd(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_zstd(struct archive *, struct la_zstream *); @@ -379,7 +379,7 @@ archive_write_set_format_7zip(struct archive *_a) zip->opt_compression = _7Z_BZIP2; #elif defined(HAVE_ZLIB_H) zip->opt_compression = _7Z_DEFLATE; -#elif HAVE_ZSTD_H +#elif HAVE_ZSTD_H && HAVE_ZSTD_compressStream zip->opt_compression = _7Z_ZSTD; #else zip->opt_compression = _7Z_COPY; @@ -841,7 +841,8 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length) return (ARCHIVE_FATAL); } if (rs == 0) { - archive_set_error(&(a->archive), 0, + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip archive"); return (ARCHIVE_FATAL); } @@ -1685,7 +1686,7 @@ file_new(struct archive_write *a, struct archive_entry *entry, const char* linkpath; linkpath = archive_entry_symlink_utf8(entry); if (linkpath == NULL) { - free(file); + file_free(file); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "symlink path could not be converted to UTF-8"); return (ARCHIVE_FAILED); diff --git a/libarchive-clib/c/archive_write_set_format_ar.c b/libarchive-clib/c/archive_write_set_format_ar.c index e5e9c80..9912835 100644 --- a/libarchive-clib/c/archive_write_set_format_ar.c +++ b/libarchive-clib/c/archive_write_set_format_ar.c @@ -172,7 +172,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) /* * If we are now at the beginning of the archive, - * we need first write the ar global header. + * we have to write the ar global header first. */ if (!ar->wrote_global_header) { __archive_write_output(a, "!\n", 8); diff --git a/libarchive-clib/c/archive_write_set_format_cpio_binary.c b/libarchive-clib/c/archive_write_set_format_cpio_binary.c index 4777e75..b0864a0 100644 --- a/libarchive-clib/c/archive_write_set_format_cpio_binary.c +++ b/libarchive-clib/c/archive_write_set_format_cpio_binary.c @@ -380,12 +380,12 @@ write_header(struct archive_write *a, struct archive_entry *entry) { struct cpio *cpio; const char *p, *path; - int pathlength, ret, ret_final; + int ret, ret_final; int64_t ino; struct cpio_binary_header h; struct archive_string_conv *sconv; struct archive_entry *entry_main; - size_t len; + size_t len, pathlength; cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; @@ -423,7 +423,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) ret_final = ARCHIVE_WARN; } /* Include trailing null */ - pathlength = (int)len + 1; + pathlength = len + 1; h.h_magic = la_swap16(070707); h.h_dev = la_swap16(archive_entry_dev(entry)); @@ -472,7 +472,13 @@ write_header(struct archive_write *a, struct archive_entry *entry) h.h_majmin = 0; h.h_mtime = la_swap32((uint32_t)archive_entry_mtime(entry)); - h.h_namesize = la_swap16(pathlength); + if (pathlength > 0xffff) { + archive_set_error(&a->archive, ERANGE, + "Filename is too long for cpio format"); + ret_final = ARCHIVE_FAILED; + goto exit_write_header; + } + h.h_namesize = la_swap16((uint16_t)pathlength); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) diff --git a/libarchive-clib/c/archive_write_set_format_cpio_newc.c b/libarchive-clib/c/archive_write_set_format_cpio_newc.c index d72434d..e281417 100644 --- a/libarchive-clib/c/archive_write_set_format_cpio_newc.c +++ b/libarchive-clib/c/archive_write_set_format_cpio_newc.c @@ -219,11 +219,11 @@ write_header(struct archive_write *a, struct archive_entry *entry) int64_t ino; struct cpio *cpio; const char *p, *path; - int pathlength, ret, ret_final; + int ret, ret_final; char h[c_header_size]; struct archive_string_conv *sconv; struct archive_entry *entry_main; - size_t len; + size_t len, pathlength; int pad; cpio = (struct cpio *)a->format_data; @@ -261,7 +261,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } - pathlength = (int)len + 1; /* Include trailing null. */ + pathlength = len + 1; /* Include trailing null. */ memset(h, 0, c_header_size); format_hex(0x070701, h + c_magic_offset, c_magic_size); @@ -292,7 +292,12 @@ write_header(struct archive_write *a, struct archive_entry *entry) format_hex(0, h + c_rdevminor_offset, c_rdevminor_size); } format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); - format_hex(pathlength, h + c_namesize_offset, c_namesize_size); + if (format_hex(pathlength, h + c_namesize_offset, c_namesize_size)) { + archive_set_error(&a->archive, ERANGE, + "Filename is too long for cpio format"); + ret_final = ARCHIVE_FAILED; + goto exit_write_header; + } format_hex(0, h + c_checksum_offset, c_checksum_size); /* Non-regular files don't store bodies. */ diff --git a/libarchive-clib/c/archive_write_set_format_cpio_odc.c b/libarchive-clib/c/archive_write_set_format_cpio_odc.c index ffac716..6b9a7f4 100644 --- a/libarchive-clib/c/archive_write_set_format_cpio_odc.c +++ b/libarchive-clib/c/archive_write_set_format_cpio_odc.c @@ -277,12 +277,12 @@ write_header(struct archive_write *a, struct archive_entry *entry) { struct cpio *cpio; const char *p, *path; - int pathlength, ret, ret_final; + int ret, ret_final; int64_t ino; char h[76]; struct archive_string_conv *sconv; struct archive_entry *entry_main; - size_t len; + size_t len, pathlength; cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; @@ -320,7 +320,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) ret_final = ARCHIVE_WARN; } /* Include trailing null. */ - pathlength = (int)len + 1; + pathlength = len + 1; memset(h, 0, sizeof(h)); format_octal(070707, h + c_magic_offset, c_magic_size); @@ -351,7 +351,12 @@ write_header(struct archive_write *a, struct archive_entry *entry) else format_octal(0, h + c_rdev_offset, c_rdev_size); format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); - format_octal(pathlength, h + c_namesize_offset, c_namesize_size); + if (format_octal((int64_t)pathlength, h + c_namesize_offset, c_namesize_size)) { + archive_set_error(&a->archive, ERANGE, + "Filename is too long for cpio format"); + ret_final = ARCHIVE_FAILED; + goto exit_write_header; + } /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) diff --git a/libarchive-clib/c/archive_write_set_format_gnutar.c b/libarchive-clib/c/archive_write_set_format_gnutar.c index b67007a..f2ba4a0 100644 --- a/libarchive-clib/c/archive_write_set_format_gnutar.c +++ b/libarchive-clib/c/archive_write_set_format_gnutar.c @@ -321,7 +321,8 @@ archive_write_gnutar_header(struct archive_write *a, const wchar_t *wp; wp = archive_entry_pathname_w(entry); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); diff --git a/libarchive-clib/c/archive_write_set_format_iso9660.c b/libarchive-clib/c/archive_write_set_format_iso9660.c index 359a273..e3cf9aa 100644 --- a/libarchive-clib/c/archive_write_set_format_iso9660.c +++ b/libarchive-clib/c/archive_write_set_format_iso9660.c @@ -119,6 +119,8 @@ static const unsigned char zisofs_magic[8] = { #define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */ #define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS) +#define MAX_JOLIET_ID_NUM 46656 /* 3 base 36 digits */ + /* * Manage extra records. */ @@ -764,7 +766,7 @@ struct iso9660 { #ifdef HAVE_ZLIB_H /* * Copy a compressed file to iso9660.zisofs.temp_fd - * and also copy a uncompressed file(original file) to + * and also copy an uncompressed file(original file) to * iso9660.temp_fd . If the number of logical block * of the compressed file is less than the number of * logical block of the uncompressed file, use it and @@ -1008,7 +1010,7 @@ static int idr_start(struct archive_write *, struct idr *, static void idr_register(struct idr *, struct isoent *, int, int); static void idr_extend_identifier(struct idrent *, int, int); -static void idr_resolve(struct idr *, void (*)(unsigned char *, int)); +static int idr_resolve(struct idr *, void (*)(unsigned char *, int)); static void idr_set_num(unsigned char *, int); static void idr_set_num_beutf16(unsigned char *, int); static int isoent_gen_iso9660_identifier(struct archive_write *, @@ -1168,6 +1170,7 @@ archive_write_set_format_iso9660(struct archive *_a) iso9660->cur_dirent = iso9660->primary.rootent; archive_string_init(&(iso9660->cur_dirstr)); if (archive_string_ensure(&(iso9660->cur_dirstr), 1) == NULL) { + free(iso9660->cur_dirent); free(iso9660); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); @@ -3036,8 +3039,8 @@ set_directory_record_rr(unsigned char *bp, int dr_len, const char *sl; char sl_last; - if (extra_space(&ctl) < 7) - bp = extra_next_record(&ctl, 7); + if (extra_space(&ctl) < 12) + bp = extra_next_record(&ctl, 12); sl = file->symlink.s; sl_last = '\0'; if (bp != NULL) { @@ -4281,9 +4284,11 @@ _write_path_table(struct archive_write *a, int type_m, int depth, set_num_731(bp+3, np->dir_location); /* Parent Directory Number */ if (type_m) - set_num_722(bp+7, np->parent->dir_number); + set_num_722(bp+7, + np->parent != NULL ? np->parent->dir_number : 0); else - set_num_721(bp+7, np->parent->dir_number); + set_num_721(bp+7, + np->parent != NULL ? np->parent->dir_number : 0); /* Directory Identifier */ if (np->identifier == NULL) bp[9] = 0; @@ -4910,20 +4915,29 @@ isofile_gen_utility_names(struct archive_write *a, struct isofile *file) * --> 'dir/dir2/' */ char *rp = p -1; + size_t off; + for (off = 4; p[off] == '/'; off++) + ; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { - strcpy(rp, p+3); + memmove(rp + 1, p + off, strlen(p + off) + 1); p = rp; } else { - strcpy(dirname, p+4); + memmove(dirname, p + off, strlen(p + off) + 1); p = dirname; } } else p++; + } else if (p == dirname && p[0] == '.' && p[1] == '.' && p[2] == '/') { + size_t off; + for (off = 3; p[off] == '/'; off++) + ; + memmove(dirname, p + off, strlen(p + off) + 1); + p = dirname; } else p++; } @@ -5526,7 +5540,7 @@ isoent_setup_file_location(struct iso9660 *iso9660, int location) static int get_path_component(char *name, size_t n, const char *fn) { - char *p; + const char *p; size_t l; p = strchr(fn, '/'); @@ -5903,21 +5917,22 @@ idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff) static void idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize) { - unsigned char *p; - int wnp_ext_off; - - wnp_ext_off = wnp->isoent->ext_off; - if (wnp->noff + numsize != wnp_ext_off) { - p = (unsigned char *)wnp->isoent->identifier; - /* Extend the filename; foo.c --> foo___.c */ - memmove(p + wnp->noff + numsize, p + wnp_ext_off, + if (wnp->noff + numsize != wnp->isoent->ext_off) { + /* + * Extend the filename; foo.c --> foo___.c + * + * The caller must verify that enough memory is available. + */ + memmove(wnp->isoent->identifier + wnp->noff + numsize, + wnp->isoent->identifier + wnp->isoent->ext_off, wnp->isoent->ext_len + nullsize); - wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize; - wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len; + wnp->isoent->ext_off = wnp->noff + numsize; + wnp->isoent->id_len = + wnp->isoent->ext_off + wnp->isoent->ext_len; } } -static void +static int idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) { struct idrent *n; @@ -5927,10 +5942,13 @@ idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) idr_extend_identifier(n, idr->num_size, idr->null_size); p = (unsigned char *)n->isoent->identifier + n->noff; do { + if (n->avail->rename_num >= MAX_JOLIET_ID_NUM) + return (-ERANGE); fsetnum(p, n->avail->rename_num++); } while (!__archive_rb_tree_insert_node( &(idr->rbtree), &(n->rbnode))); } + return (0); } static void @@ -5990,6 +6008,10 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_iso9660, isoent_cmp_key_iso9660 }; + const int num_size = 3; + const int dot_size = 1; + const int version_size = 2; + const int null_size = 1; if (isoent->children.cnt == 0) return (0); @@ -6030,7 +6052,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, fnmax = ffmax = dnmax = 207; } - r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops); + r = idr_start(a, idr, isoent->children.cnt, ffmax, num_size, null_size, &rb_ops); if (r < 0) return (r); @@ -6039,7 +6061,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, int ext_off, noff, weight; l = (int)np->file->basename.length; - p = malloc(l+31+2+1); + p = malloc(l + num_size + dot_size + version_size + null_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); @@ -6189,13 +6211,19 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, noff = ext_off - 1; else noff = ext_off; + if (noff < 0) + noff = 0; } /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier. */ - idr_resolve(idr, idr_set_num); + r = idr_resolve(idr, idr_set_num); + if (r < 0) { + archive_set_error(&a->archive, -r, "Too many duplicated identifiers"); + return (ARCHIVE_FATAL); + } /* Add a period and a version number to identifiers. */ for (np = isoent->children.first; np != NULL; np = np->chnext) { @@ -6239,6 +6267,8 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_joliet, isoent_cmp_key_joliet }; + const int num_size = 6; + const int null_size = 2; if (isoent->children.cnt == 0) return (0); @@ -6249,7 +6279,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, else ffmax = 128; - r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops); + r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, num_size, null_size, &rb_ops); if (r < 0) return (r); @@ -6265,7 +6295,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, if ((l = np->file->basename_utf16.length) > ffmax) l = ffmax; - p = malloc((l+1)*2); + p = malloc(l + num_size + null_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); @@ -6334,12 +6364,18 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, noff = ext_off - 2; else noff = ext_off; + if (noff < 0) + noff = 0; /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier with Joliet Volume. */ - idr_resolve(idr, idr_set_num_beutf16); + r = idr_resolve(idr, idr_set_num_beutf16); + if (r < 0) { + archive_set_error(&a->archive, -r, "Too many duplicated identifiers"); + return (ARCHIVE_FATAL); + } return (ARCHIVE_OK); } @@ -6727,7 +6763,12 @@ isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved, /* * The mvent becomes a child of the rr_moved entry. */ - isoent_add_child_tail(rrmoved, mvent); + if (!isoent_add_child_tail(rrmoved, mvent)) { + _isoent_free(mvent); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unable to insert rr_moved entry"); + return (ARCHIVE_FATAL); + } archive_entry_set_nlink(rrmoved->file->entry, archive_entry_nlink(rrmoved->file->entry) + 1); /* @@ -6820,7 +6861,15 @@ _compare_path_table(const void *v1, const void *v2) p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ - cmp = p1->parent->dir_number - p2->parent->dir_number; + if (p1->parent == NULL || p2->parent == NULL) { + if (p1->parent == p2->parent) + cmp = 0; + else if (p1->parent == NULL) + return (-1); + else + return (1); + } else + cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); @@ -6863,7 +6912,15 @@ _compare_path_table_joliet(const void *v1, const void *v2) p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ - cmp = p1->parent->dir_number - p2->parent->dir_number; + if (p1->parent == NULL || p2->parent == NULL) { + if (p1->parent == p2->parent) + cmp = 0; + else if (p1->parent == NULL) + return (-1); + else + return (1); + } else + cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); diff --git a/libarchive-clib/c/archive_write_set_format_mtree.c b/libarchive-clib/c/archive_write_set_format_mtree.c index a4ffec8..ed53849 100644 --- a/libarchive-clib/c/archive_write_set_format_mtree.c +++ b/libarchive-clib/c/archive_write_set_format_mtree.c @@ -307,14 +307,14 @@ static const uint32_t crctab[] = { static const unsigned char safe_char[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ - /* !"$%&'()*+,-./ EXCLUSION:0x20( ) 0x23(#) */ - 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ - /* 0123456789:;<>? EXCLUSION:0x3d(=) */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ + /* !"$%&'()+,-./ EXCLUSION:0x20( ) 0x23(#) 0x2a(*) */ + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 20 - 2F */ + /* 0123456789:;<> EXCLUSION:0x3d(=) 0x3f(?) */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, /* 30 - 3F */ /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ - /* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\) */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */ + /* PQRSTUVWXYZ]^_ EXCLUSION:0x5b([) 0x5c(\) */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, /* 50 - 5F */ /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ /* pqrstuvwxyz{|}~ */ @@ -801,6 +801,8 @@ mtree_entry_new(struct archive_write *a, struct archive_entry *entry, archive_strcpy(&me->symlink, s); me->nlink = archive_entry_nlink(entry); me->filetype = archive_entry_filetype(entry); + if (me->filetype == AE_IFLNK && me->symlink.s == NULL) + archive_strcpy(&me->symlink, ""); me->mode = archive_entry_mode(entry) & 07777; me->uid = archive_entry_uid(entry); me->gid = archive_entry_gid(entry); @@ -988,7 +990,8 @@ write_mtree_entry(struct archive_write *a, struct mtree_entry *me) * a full pathname. */ mtree_quote(str, me->parentdir.s); - archive_strappend_char(str, '/'); + if (strcmp(me->basename.s, ".") != 0) + archive_strappend_char(str, '/'); } mtree_quote(str, me->basename.s); @@ -1145,6 +1148,8 @@ write_mtree_entry_tree(struct archive_write *a) int ret; do { + if (np->dir_info == NULL) + break; if (mtree->output_global_set) { /* * Collect attribute information to know which value @@ -1611,6 +1616,7 @@ sum_update(struct mtree_writer *mtree, const void *buff, size_t n) static void sum_final(struct mtree_writer *mtree, struct reg_info *reg) { + struct ae_digest digest; if (mtree->compute_sum & F_CKSUM) { uint64_t len; @@ -1620,40 +1626,34 @@ sum_final(struct mtree_writer *mtree, struct reg_info *reg) reg->crc = ~mtree->crc; } #ifdef ARCHIVE_HAS_MD5 - if ((mtree->compute_sum & F_MD5) - && !(reg->mset_digest & AE_MSET_DIGEST_MD5)) + if (mtree->compute_sum & F_MD5) - archive_md5_final(&mtree->md5ctx, reg->digest.md5); + archive_md5_final(&mtree->md5ctx, (reg->mset_digest & AE_MSET_DIGEST_MD5) ? digest.md5 : reg->digest.md5); #endif #ifdef ARCHIVE_HAS_RMD160 - if ((mtree->compute_sum & F_RMD160) - && !(reg->mset_digest & AE_MSET_DIGEST_RMD160)) + if (mtree->compute_sum & F_RMD160) - archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160); + archive_rmd160_final(&mtree->rmd160ctx, (reg->mset_digest & AE_MSET_DIGEST_RMD160) ? digest.rmd160 : reg->digest.rmd160); #endif #ifdef ARCHIVE_HAS_SHA1 - if ((mtree->compute_sum & F_SHA1) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA1)) + if (mtree->compute_sum & F_SHA1) - archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1); + archive_sha1_final(&mtree->sha1ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA1) ? digest.sha1 : reg->digest.sha1); #endif #ifdef ARCHIVE_HAS_SHA256 - if ((mtree->compute_sum & F_SHA256) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA256)) + if (mtree->compute_sum & F_SHA256) - archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256); + archive_sha256_final(&mtree->sha256ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA256) ? digest.sha256 : reg->digest.sha256); #endif #ifdef ARCHIVE_HAS_SHA384 - if ((mtree->compute_sum & F_SHA384) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA384)) + if (mtree->compute_sum & F_SHA384) - archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384); + archive_sha384_final(&mtree->sha384ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA384) ? digest.sha384 : reg->digest.sha384); #endif #ifdef ARCHIVE_HAS_SHA512 - if ((mtree->compute_sum & F_SHA512) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA512)) + if (mtree->compute_sum & F_SHA512) - archive_sha512_final(&mtree->sha512ctx, reg->digest.sha512); + archive_sha512_final(&mtree->sha512ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA512) ? digest.sha512 : reg->digest.sha512); #endif /* Save what types of sum are computed. */ reg->compute_sum = mtree->compute_sum; @@ -1886,20 +1886,29 @@ mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file, * --> 'dir/dir2/' */ char *rp = p -1; + size_t off; + for (off = 4; p[off] == '/'; off++) + ; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { - strcpy(rp, p+3); + memmove(rp + 1, p + off, strlen(p + off) + 1); p = rp; } else { - strcpy(dirname, p+4); + memmove(dirname, p + off, strlen(p + off) + 1); p = dirname; } } else p++; + } else if (p == dirname && p[0] == '.' && p[1] == '.' && p[2] == '/') { + size_t off; + for (off = 3; p[off] == '/'; off++) + ; + memmove(dirname, p + off, strlen(p + off) + 1); + p = dirname; } else p++; } @@ -2041,7 +2050,7 @@ mtree_entry_find_child(struct mtree_entry *parent, const char *child_name) static int get_path_component(char *name, size_t n, const char *fn) { - char *p; + const char *p; size_t l; p = strchr(fn, '/'); @@ -2079,6 +2088,11 @@ mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep) file = *filep; if (file->parentdir.length == 0 && file->basename.length == 1 && file->basename.s[0] == '.') { + if (file->filetype != AE_IFDIR) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Root entry '.' must be a directory"); + return (ARCHIVE_FAILED); + } file->parent = file; if (mtree->root != NULL) { np = mtree->root; diff --git a/libarchive-clib/c/archive_write_set_format_pax.c b/libarchive-clib/c/archive_write_set_format_pax.c index f3589d6..cda1ffd 100644 --- a/libarchive-clib/c/archive_write_set_format_pax.c +++ b/libarchive-clib/c/archive_write_set_format_pax.c @@ -40,6 +40,7 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" @@ -676,7 +677,8 @@ archive_write_pax_header(struct archive_write *a, const wchar_t *wp; wp = archive_entry_pathname_w(entry_original); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); @@ -1934,17 +1936,17 @@ url_encode(const char *in) for (s = in; *s != '\0'; s++) { if (*s < 33 || *s > 126 || *s == '%' || *s == '=') { - if (SIZE_MAX - out_len < 4) + if (archive_ckd_add_size(&out_len, out_len, 3)) return (NULL); - out_len += 3; } else { - if (SIZE_MAX - out_len < 2) + if (archive_ckd_add_size(&out_len, out_len, 1)) return (NULL); - out_len++; } } - out = malloc(out_len + 1); + if (archive_ckd_add_size(&out_len, out_len, 1)) + return (NULL); + out = malloc(out_len); if (out == NULL) return (NULL); diff --git a/libarchive-clib/c/archive_write_set_format_shar.c b/libarchive-clib/c/archive_write_set_format_shar.c index f6f28de..ad8d5b7 100644 --- a/libarchive-clib/c/archive_write_set_format_shar.c +++ b/libarchive-clib/c/archive_write_set_format_shar.c @@ -38,6 +38,7 @@ #endif #include "archive.h" +#include "archive_endian.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" @@ -173,6 +174,10 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) /* Save the entry for the closing. */ archive_entry_free(shar->entry); shar->entry = archive_entry_clone(entry); + if (shar->entry == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } name = archive_entry_pathname(entry); /* Handle some preparatory issues. */ @@ -408,9 +413,9 @@ static void uuencode_group(const char _in[3], char out[4]) { const unsigned char *in = (const unsigned char *)_in; - int t; + uint32_t t; - t = (in[0] << 16) | (in[1] << 8) | in[2]; + t = archive_be24dec(in); out[0] = UUENC( 0x3f & (t >> 18) ); out[1] = UUENC( 0x3f & (t >> 12) ); out[2] = UUENC( 0x3f & (t >> 6) ); diff --git a/libarchive-clib/c/archive_write_set_format_ustar.c b/libarchive-clib/c/archive_write_set_format_ustar.c index 97724c1..e84152e 100644 --- a/libarchive-clib/c/archive_write_set_format_ustar.c +++ b/libarchive-clib/c/archive_write_set_format_ustar.c @@ -281,7 +281,8 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) const wchar_t *wp; wp = archive_entry_pathname_w(entry); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); diff --git a/libarchive-clib/c/archive_write_set_format_v7tar.c b/libarchive-clib/c/archive_write_set_format_v7tar.c index 37ba73a..91c5e8e 100644 --- a/libarchive-clib/c/archive_write_set_format_v7tar.c +++ b/libarchive-clib/c/archive_write_set_format_v7tar.c @@ -259,7 +259,8 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) const wchar_t *wp; wp = archive_entry_pathname_w(entry); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); diff --git a/libarchive-clib/c/archive_write_set_format_warc.c b/libarchive-clib/c/archive_write_set_format_warc.c index 3d22e1f..2192995 100644 --- a/libarchive-clib/c/archive_write_set_format_warc.c +++ b/libarchive-clib/c/archive_write_set_format_warc.c @@ -245,6 +245,7 @@ _warc_header(struct archive_write *a, struct archive_entry *entry) r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh); if (r < 0) { /* don't bother */ + archive_string_free(&hdr); archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -371,7 +372,7 @@ _popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr) static const char _uri[] = ""; static const char _fil[] = "file://"; const char *u; - char *chk = strchr(hdr.tgturi, ':'); + const char *chk = strchr(hdr.tgturi, ':'); if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') { /* yep, it's definitely a URI */ diff --git a/libarchive-clib/c/archive_write_set_format_xar.c b/libarchive-clib/c/archive_write_set_format_xar.c index 6ecccde..98bc495 100644 --- a/libarchive-clib/c/archive_write_set_format_xar.c +++ b/libarchive-clib/c/archive_write_set_format_xar.c @@ -819,9 +819,7 @@ xar_finish_entry(struct archive_write *a) if (s > a->null_length) s = a->null_length; w = xar_write_data(a, a->nulls, s); - if (w > 0) - xar->bytes_remaining -= w; - else + if (w <= 0) return ((int)w); } file = xar->cur_file; @@ -1110,30 +1108,27 @@ make_fflags_entry(struct archive_write *a, struct xml_writer *writer, { NULL, NULL} }; const struct flagentry *fe, *flagentry; -#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd)) - const struct flagentry *avail[FLAGENTRY_MAXSIZE]; const char *p; - int i, n, r; + int r, started; if (strcmp(element, "ext2") == 0) flagentry = flagext2; else flagentry = flagbsd; - n = 0; p = fflags_text; + started = 0; do { - const char *cp; + const char *cp, *name = NULL; cp = strchr(p, ','); if (cp == NULL) cp = p + strlen(p); for (fe = flagentry; fe->name != NULL; fe++) { - if (fe->name[cp - p] != '\0' - || p[0] != fe->name[0]) - continue; if (strncmp(p, fe->name, cp - p) == 0) { - avail[n++] = fe; + if (fe->name[cp - p] != '\0') + continue; + name = fe->xarname; break; } } @@ -1141,23 +1136,26 @@ make_fflags_entry(struct archive_write *a, struct xml_writer *writer, p = cp + 1; else p = NULL; - } while (p != NULL); - if (n > 0) { - r = xml_writer_start_element(writer, element); - if (r < 0) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "xml_writer_start_element() failed: %d", r); - return (ARCHIVE_FATAL); - } - for (i = 0; i < n; i++) { - r = xmlwrite_string(a, writer, - avail[i]->xarname, NULL); + if (name != NULL) { + if (!started) { + r = xml_writer_start_element(writer, element); + if (r < 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "xml_writer_start_element()" + " failed: %d", r); + return (ARCHIVE_FATAL); + } + started = 1; + } + r = xmlwrite_string(a, writer, name, NULL); if (r != ARCHIVE_OK) return (r); } + } while (p != NULL); + if (started) { r = xml_writer_end_element(writer); if (r < 0) { archive_set_error(&a->archive, @@ -1439,7 +1437,7 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer, } /* - * Make a mtime entry, "". + * Make an mtime entry, "". */ if (archive_entry_mtime_is_set(file->entry)) { r = xmlwrite_time(a, writer, "mtime", @@ -1866,7 +1864,8 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length) return (ARCHIVE_FATAL); } if (rs == 0) { - archive_set_error(&(a->archive), 0, + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated xar archive"); return (ARCHIVE_FATAL); } @@ -2205,20 +2204,29 @@ file_gen_utility_names(struct archive_write *a, struct file *file) * --> 'dir/dir2/' */ char *rp = p -1; + size_t off; + for (off = 4; p[off] == '/'; off++) + ; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { - strcpy(rp, p+3); + memmove(rp + 1, p + off, strlen(p + off) + 1); p = rp; } else { - strcpy(dirname, p+4); + memmove(dirname, p + off, strlen(p + off) + 1); p = dirname; } } else p++; + } else if (p == dirname && p[0] == '.' && p[1] == '.' && p[2] == '/') { + size_t off; + for (off = 3; p[off] == '/'; off++) + ; + memmove(dirname, p + off, strlen(p + off) + 1); + p = dirname; } else p++; } @@ -2272,7 +2280,7 @@ file_gen_utility_names(struct archive_write *a, struct file *file) static int get_path_component(char *name, int n, const char *fn) { - char *p; + const char *p; int l; p = strchr(fn, '/'); @@ -2378,10 +2386,24 @@ file_tree(struct archive_write *a, struct file **filepp) archive_string_init(&as); archive_strncat(&as, p, fn - p + l); - if (as.s[as.length-1] == '/') { + if (as.length > 0 && as.s[as.length-1] == '/') { as.s[as.length-1] = '\0'; as.length--; } + if (as.length == 0) { + archive_string_free(&as); + fn += strspn(fn, "/"); + l = get_path_component(name, sizeof(name), fn); + if (l < 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "A name buffer is too small"); + file_free(file); + *filepp = NULL; + return (ARCHIVE_FATAL); + } + continue; + } vp = file_create_virtual_dir(a, xar, as.s); if (vp == NULL) { archive_string_free(&as); diff --git a/libarchive-clib/c/archive_write_set_format_zip.c b/libarchive-clib/c/archive_write_set_format_zip.c index 81d3db0..33d60ce 100644 --- a/libarchive-clib/c/archive_write_set_format_zip.c +++ b/libarchive-clib/c/archive_write_set_format_zip.c @@ -797,6 +797,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) unsigned char *e; unsigned char *cd_extra; size_t filename_length; + const char *path; const char *slink = NULL; size_t slink_size = 0; struct archive_string_conv *sconv = get_sconv(a, zip); @@ -960,6 +961,19 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) } filename_length = path_length(zip->entry); + /* Reject empty or overlong pathnames */ + path = archive_entry_pathname(zip->entry); + if (path == NULL || path[0] == '\0') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP format requires a non-empty pathname"); + return (ARCHIVE_FAILED); + } + if (filename_length > 0xffff) { + archive_set_error(&a->archive, ENAMETOOLONG, + "Pathname too long for ZIP format"); + return (ARCHIVE_FAILED); + } + /* Determine appropriate compression and size for this entry. */ if (type == AE_IFLNK) { slink = archive_entry_symlink(zip->entry); @@ -2304,7 +2318,7 @@ write_path(struct archive_entry *entry, struct archive_write *archive) written_bytes += strlen(path); /* Folders are recognized by a trailing slash. */ - if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) { + if ((type == AE_IFDIR) && (path[strlen(path) - 1] != '/')) { ret = __archive_write_output(archive, "/", 1); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); diff --git a/libarchive-clib/c/config.h.in b/libarchive-clib/c/config.h.in index 77cf778..2a641ba 100644 --- a/libarchive-clib/c/config.h.in +++ b/libarchive-clib/c/config.h.in @@ -1122,6 +1122,10 @@ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif +/* Enable extensions on Cosmopolitan Libc. */ +#ifndef _COSMO_SOURCE +# undef _COSMO_SOURCE +#endif /* Enable general extensions on macOS. */ #ifndef _DARWIN_C_SOURCE # undef _DARWIN_C_SOURCE diff --git a/libarchive-clib/c/filter_fork_windows.c b/libarchive-clib/c/filter_fork_windows.c index 9e49c56..a0fd638 100644 --- a/libarchive-clib/c/filter_fork_windows.c +++ b/libarchive-clib/c/filter_fork_windows.c @@ -31,43 +31,68 @@ #include "filter_fork.h" -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) /* There are some editions of Windows ("nano server," for example) that * do not host user32.dll. If we want to keep running on those editions, * we need to delay-load WaitForInputIdle. */ -static void * -la_GetFunctionUser32(const char *name) + +static int +failing_wait(HANDLE hProcess, DWORD dwMilliseconds) { + /* An inability to wait for input idle is + * not _good_, but it is not catastrophic. */ + (void)hProcess; /* UNUSED */ + (void)dwMilliseconds; /* UNUSED */ + return WAIT_FAILED; +} + +# if _WIN32_WINNT < _WIN32_WINNT_VISTA +static int +la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds) { - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; + static DWORD (WINAPI * volatile f)(HANDLE, DWORD); + + if (f == NULL) { + HINSTANCE lib; + void *old; + DWORD (WINAPI *tmp)(HANDLE, DWORD); + lib = LoadLibrary(TEXT("user32.dll")); + tmp = (lib != NULL) ? + (PVOID)GetProcAddress(lib, "WaitForInputIdle") : + failing_wait; + old = InterlockedCompareExchangePointer((volatile PVOID *)&f, + tmp, NULL); + if (old != NULL && lib != NULL) + FreeLibrary(lib); } - if (lib == NULL) { - return NULL; - } - return (void *)GetProcAddress(lib, name); + + return (*f)(hProcess, dwMilliseconds); +} +# else +static BOOL CALLBACK +load_WaitForInputIdle(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) { + HMODULE user32 = LoadLibrary(TEXT("user32.dll")); + + (void)InitOnce; /* UNUSED */ + (void)Parameter; /* UNUSED */ + + *Context = (user32 != NULL) ? + (PVOID)GetProcAddress(user32, "WaitForInputIdle") : failing_wait; + + return TRUE; } static int la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds) { static DWORD (WINAPI *f)(HANDLE, DWORD); - static int set; + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; - if (!set) { - set = 1; - f = la_GetFunctionUser32("WaitForInputIdle"); - } + InitOnceExecuteOnce(&once, load_WaitForInputIdle, NULL, (PVOID)&f); - if (!f) { - /* An inability to wait for input idle is - * not _good_, but it is not catastrophic. */ - return WAIT_FAILED; - } return (*f)(hProcess, dwMilliseconds); } +# endif int __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, diff --git a/libarchive-clib/c/xxhash.c b/libarchive-clib/c/xxhash.c index beacd23..37dc6ff 100644 --- a/libarchive-clib/c/xxhash.c +++ b/libarchive-clib/c/xxhash.c @@ -361,6 +361,8 @@ static void* XXH32_init (U32 seed) { void* state = XXH_malloc (sizeof(struct XXH_state32_t)); + if (state == NULL) + return NULL; XXH32_resetState(state, seed); return state; } diff --git a/libarchive-clib/libarchive-clib.cabal b/libarchive-clib/libarchive-clib.cabal index c6d629c..d71d7d7 100644 --- a/libarchive-clib/libarchive-clib.cabal +++ b/libarchive-clib/libarchive-clib.cabal @@ -2,7 +2,7 @@ cabal-version: 2.0 name: libarchive-clib -- this is actually corresponding to upstream's 3.8.6, -- but we had to fix configure.ac and PVP has no patch versions -version: 3.8.7 +version: 3.8.8 license: OtherLicense license-file: LICENSE copyright: Copyright: (c) 2018-2020 Vanessa McHale @@ -80,6 +80,8 @@ library default-language: Haskell2010 if os(windows) cc-options: -U__GCC_ASM_FLAG_OUTPUTS__ + extra-libraries: bcrypt + extra-libraries-static: bcrypt if arch(i386) cc-options: -D_Alignas(t)=__attribute__((__aligned__(t)))