From 283ab0e7ea37864aa79c7f2c88e448e2a6696bc2 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Mon, 6 Nov 2023 19:29:40 +0100 Subject: [PATCH] CI updates and assorted cleanups --- .github/workflows/ci.yml | 4 +- .github/workflows/misc-spell-check.yml | 2 +- .github/workflows/weekly-ci-bs-by-hand.yml | 73 +++++++- CMakeLists.txt | 3 +- misc/scripts/build_upx_by_hand.sh | 42 +++-- src/compress/compress.cpp | 20 +- src/compress/compress.h | 48 ++--- src/compress/compress_bzip2.cpp | 208 +++++++++++++++++++++ src/compress/compress_lzma.cpp | 5 +- src/compress/compress_ucl.cpp | 4 +- src/compress/compress_zlib.cpp | 5 +- src/compress/compress_zstd.cpp | 5 +- src/conf.h | 53 ++++-- src/except.cpp | 14 +- src/except.h | 4 + src/help.cpp | 8 + src/pefile.cpp | 2 +- src/ui.cpp | 2 +- src/ui.h | 2 +- src/util/snprintf.cpp | 38 ++++ src/util/snprintf.h | 5 + 21 files changed, 462 insertions(+), 85 deletions(-) create mode 100644 src/compress/compress_bzip2.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7710be6e..360ffffc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -327,7 +327,7 @@ jobs: git rev-parse --short=12 HEAD > %BDIR%\upx\.GITREV.txt @REM ===== build bzip2 ===== cd %BDIR%\bzip2 - @rem %RUN_CL% -J -O2 -W4 -wd4127 -wd4244 -wd4267 -WX %DEFS% -c %H%\vendor\bzip2\*.c + @rem %RUN_CL% -J -O2 -W4 -wd4127 -wd4244 -wd4267 -WX %DEFS% -DBZ_NO_STDIO -c %H%\vendor\bzip2\*.c @rem %RUN_LIB% -out:bzip2.lib *.obj @REM ===== build UCL ===== cd %BDIR%\ucl @@ -348,7 +348,7 @@ jobs: set s=%H%\src cat .GITREV.txt set /p GITREV=<.GITREV.txt - set UPX_DEFS=-DUPX_CONFIG_DISABLE_WSTRICT=0 -DUPX_CONFIG_DISABLE_WERROR=0 -DWITH_BZIP2=0 -DWITH_ZSTD=0 + set UPX_DEFS=-DUPX_CONFIG_DISABLE_WSTRICT=0 -DUPX_CONFIG_DISABLE_WERROR=0 -DWITH_THREADS=0 -DWITH_BZIP2=0 -DWITH_ZSTD=0 set UPX_LIBS=%BDIR%\ucl\ucl.lib %BDIR%\zlib\zlib.lib @rem set UPX_LIBS=%BDIR%\bzip2\bzip2.lib %BDIR%\ucl\ucl.lib %BDIR%\zlib\zlib.lib %BDIR%\zstd\zstd.lib set sources=%s%\*.cpp %s%\check\*.cpp %s%\compress\*.cpp %s%\console\*.cpp %s%\filter\*.cpp %s%\util\*.cpp diff --git a/.github/workflows/misc-spell-check.yml b/.github/workflows/misc-spell-check.yml index ac473d16..0d76b8b9 100644 --- a/.github/workflows/misc-spell-check.yml +++ b/.github/workflows/misc-spell-check.yml @@ -17,5 +17,5 @@ jobs: uses: actions/checkout@v4 with: { submodules: false } - name: 'Spell check with crate-ci/typos' - uses: crate-ci/typos@47dd2976043bd5c76a33aa9300b328a176a1d6f7 # v1.16.21 + uses: crate-ci/typos@0d04ce91a7a8436a6e3c589750514ac586632192 # v1.16.22 with: { config: ./.github/typos_config.toml } diff --git a/.github/workflows/weekly-ci-bs-by-hand.yml b/.github/workflows/weekly-ci-bs-by-hand.yml index d4ae1f09..5e7c9faf 100644 --- a/.github/workflows/weekly-ci-bs-by-hand.yml +++ b/.github/workflows/weekly-ci-bs-by-hand.yml @@ -142,7 +142,7 @@ jobs: cd "upx with space"/build/by-hand bash "$testsuite" - job-by-hand-windows: # uses a POSIX-compliant shell + job-by-hand-windows-clang: # uses a POSIX-compliant shell # ...and also uses a subdirectory "upx with space" in order to detect possible quoting issues if: github.repository_owner == 'upx' strategy: @@ -151,7 +151,7 @@ jobs: include: - { os: windows-2019 } - { os: windows-2022 } - name: ${{ format('by-hand cc {0}', matrix.os) }} + name: ${{ format('by-hand clang {0}', matrix.os) }} runs-on: ${{ matrix.os }} steps: - run: git config --global core.autocrlf false @@ -160,7 +160,7 @@ jobs: run: | git clone --branch "$GITHUB_REF_NAME" --depth 1 "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" "upx with space" git -C "upx with space" submodule update --init - - name: 'Build by-hand with bash - clang' + - name: 'Build by-hand with bash' shell: bash run: | xflags="-static -Wall -Wextra -Werror" @@ -169,9 +169,72 @@ jobs: export zlib_extra_flags="-DHAVE_VSNPRINTF" export AR_LIBFILE=upx_submodules.lib CC="clang $xflags" CXX="clang++ -std=gnu++17 $xflags" bash "./upx with space/misc/scripts/build_upx_by_hand.sh" - - name: 'Build by-hand with bash - gcc' - if: success() || failure() # run this step even if the previous step failed + + job-by-hand-windows-gcc: # uses a POSIX-compliant shell + # ...and also uses a subdirectory "upx with space" in order to detect possible quoting issues + if: github.repository_owner == 'upx' + strategy: + fail-fast: false + matrix: + include: + - { os: windows-2019 } + - { os: windows-2022 } + name: ${{ format('by-hand gcc {0}', matrix.os) }} + runs-on: ${{ matrix.os }} + steps: + - run: git config --global core.autocrlf false + - name: ${{ format('Check out UPX {0} source code', github.ref_name) }} + shell: bash + run: | + git clone --branch "$GITHUB_REF_NAME" --depth 1 "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" "upx with space" + git -C "upx with space" submodule update --init + - name: 'Build by-hand with bash' shell: bash run: | xflags="-static -Wall -Wextra -Werror" CC="gcc $xflags" CXX="g++ -std=gnu++17 $xflags" bash "./upx with space/misc/scripts/build_upx_by_hand.sh" + + job-by-hand-windows-msvc: # uses a POSIX-compliant shell + # ...and also uses a subdirectory "upx with space" in order to detect possible quoting issues + if: github.repository_owner == 'upx' + strategy: + fail-fast: false + matrix: + include: + # clang-cl + - { os: windows-2019, vsversion: 2019, arch: amd64, clang_cl: true } + - { os: windows-2022, vsversion: 2022, arch: amd64, clang_cl: true } + # msvc + - { os: windows-2019, vsversion: 2019, arch: amd64 } + - { os: windows-2019, vsversion: 2019, arch: amd64_arm64 } + - { os: windows-2019, vsversion: 2019, arch: amd64_x86 } + - { os: windows-2022, vsversion: 2022, arch: amd64 } + - { os: windows-2022, vsversion: 2022, arch: amd64_arm64 } + - { os: windows-2022, vsversion: 2022, arch: amd64_x86 } + name: ${{ format('by-hand vs{0} {1} {2}', matrix.vsversion, matrix.arch, matrix.clang_cl && 'clang-cl' || '') }} + runs-on: ${{ matrix.os }} + steps: + - run: git config --global core.autocrlf false + - name: ${{ format('Check out UPX {0} source code', github.ref_name) }} + shell: bash + run: | + git clone --branch "$GITHUB_REF_NAME" --depth 1 "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" "upx with space" + git -C "upx with space" submodule update --init + - name: 'Set up Developer Command Prompt' + uses: ilammy/msvc-dev-cmd@cec98b9d092141f74527d0afa6feb2af698cfe89 # v1.12.1 + with: + vsversion: ${{ matrix.vsversion }} + arch: ${{ matrix.arch }} + - name: 'Build by-hand with bash' + shell: bash + run: | + X="${{ matrix.clang_cl && 'clang-cl' || 'cl' }}" + command -v cl clang-cl lib link + # using MSVC headers and libraries, so adjust settings + xflags="-MT -J -W3 -DWIN32_LEAN_AND_MEAN -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS" + export mandatory_flags= + export sensible_flags= + export zlib_extra_flags="-DHAVE_VSNPRINTF" + export AR=false + export obj_suffix=.obj + CC="$X $xflags" CXX="$X -std:c++17 -Zc:__cplusplus -EHsc $xflags" bash "./upx with space/misc/scripts/build_upx_by_hand.sh" diff --git a/CMakeLists.txt b/CMakeLists.txt index 98e947e7..3823bc29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,7 @@ if(NOT UPX_CONFIG_DISABLE_BZIP2) set(t upx_vendor_bzip2) upx_compile_target_debug_with_O2(${t}) upx_sanitize_target(${t}) +target_compile_definitions(${t} PRIVATE BZ_NO_STDIO=1) if(MSVC_FRONTEND) target_compile_options(${t} PRIVATE ${warn_WN} -wd4127 -wd4244 -wd4267 ${warn_WX}) else() @@ -488,7 +489,7 @@ if(NOT UPX_CONFIG_DISABLE_WERROR) target_compile_definitions(${t} PRIVATE UPX_CONFIG_DISABLE_WERROR=0) endif() if(NOT UPX_CONFIG_DISABLE_BZIP2) - target_compile_definitions(${t} PRIVATE WITH_BZIP2=0) # FIXME TODO + target_compile_definitions(${t} PRIVATE WITH_BZIP2=1) endif() if(NOT UPX_CONFIG_DISABLE_ZSTD) target_compile_definitions(${t} PRIVATE WITH_ZSTD=1) diff --git a/misc/scripts/build_upx_by_hand.sh b/misc/scripts/build_upx_by_hand.sh index 1adeb632..e99db694 100755 --- a/misc/scripts/build_upx_by_hand.sh +++ b/misc/scripts/build_upx_by_hand.sh @@ -8,7 +8,8 @@ set -e # Copyright (C) Markus Franz Xaver Johannes Oberhumer # -# uses optional environment variables: AR, CC, CXX, OPTIMIZE, VERBOSE, top_srcdir +# uses optional environment variables: AR, CC, CXX, OPTIMIZE, VERBOSE +# and optional settings for top_srcdir, obj_suffix and XXX_extra_flags # shell init ### set -x # enable logging @@ -24,19 +25,23 @@ CXX="${CXX:-c++ -std=gnu++17}" if test "x$AR" = "x0" || test "x$AR" = "xfalse" || test "x$AR" = "x/bin/false"; then AR="" # do not use $AR fi -# protect against security threats caused by misguided compiler "optimizations" -mandatory_flags="-fno-strict-aliasing -fno-strict-overflow -funsigned-char" -# not mandatory but good practice when using : -sensible_flags="-DWIN32_LEAN_AND_MEAN" -if test "x$OPTIMIZE" != "x" && test "x$OPTIMIZE" != "x0"; then - # not mandatory and not minimal, but usually a good idea: - sensible_flags="-Wall -O2 $sensible_flags" +if test -z "${mandatory_flags+set}"; then + # protect against security threats caused by misguided compiler "optimizations" + mandatory_flags="-fno-strict-aliasing -fno-strict-overflow -funsigned-char" +fi +if test -z "${sensible_flags+set}"; then + # not mandatory but good practice when using : + sensible_flags="-DWIN32_LEAN_AND_MEAN" + if test "x$OPTIMIZE" != "x" && test "x$OPTIMIZE" != "x0"; then + # not mandatory and not minimal, but usually a good idea: + sensible_flags="-Wall -O2 $sensible_flags" + fi fi CC="$CC $sensible_flags $mandatory_flags" CXX="$CXX $sensible_flags $mandatory_flags" # go to upx top-level directory -# HINT: set "top_srcdir" manually if your system does not have "readlink" +# HINT: set "top_srcdir" manually if your system does not have "readlink -f" if test "x$top_srcdir" = "x"; then my_argv0abs="$(readlink -fn "$my_argv0")" test "x$my_argv0abs" = "x" && exit 1 @@ -91,25 +96,31 @@ check_submodule() { } # build +upx_submodule_defs= run "+" mkdir -p "build/by-hand" if check_submodule bzip2; then - test -z "${bzip2_extra_flags+set}" && bzip2_extra_flags= + upx_submodule_defs="$upx_submodule_defs -DWITH_BZIP2" + test -z "${bzip2_extra_flags+set}" && bzip2_extra_flags="-DBZ_NO_STDIO" for f in "$rel_top_srcdir"/vendor/bzip2/*.c; do run "CC $f" $CC $bzip2_extra_flags -c "$f" done fi if check_submodule ucl; then + #upx_submodule_defs="$upx_submodule_defs -DWITH_UCL" + test -z "${ucl_extra_flags+set}" && ucl_extra_flags= for f in "$rel_top_srcdir"/vendor/ucl/src/*.c; do - run "CC $f" $CC -I"$rel_top_srcdir"/vendor/ucl/include -I"$rel_top_srcdir"/vendor/ucl -c "$f" + run "CC $f" $CC -I"$rel_top_srcdir"/vendor/ucl/include -I"$rel_top_srcdir"/vendor/ucl $ucl_extra_flags -c "$f" done fi if check_submodule zlib; then + #upx_submodule_defs="$upx_submodule_defs -DWITH_ZLIB" test -z "${zlib_extra_flags+set}" && zlib_extra_flags="-DHAVE_UNISTD_H -DHAVE_VSNPRINTF" for f in "$rel_top_srcdir"/vendor/zlib/*.c; do run "CC $f" $CC $zlib_extra_flags -c "$f" done fi if check_submodule zstd; then + upx_submodule_defs="$upx_submodule_defs -DWITH_ZSTD" test -z "${zstd_extra_flags+set}" && zstd_extra_flags="-DDYNAMIC_BMI2=0 -DZSTD_DISABLE_ASM" for f in "$rel_top_srcdir"/vendor/zstd/lib/*/*.c; do run "CC $f" $CC $zstd_extra_flags -c "$f" @@ -121,15 +132,16 @@ echo "#==== build UPX =====" run "+" cd "build/by-hand" || exit 1 rel_top_srcdir=../.. for f in "$rel_top_srcdir"/src/*.cpp "$rel_top_srcdir"/src/*/*.cpp; do - run "CXX $f" $CXX -I"$rel_top_srcdir"/vendor -c "$f" + run "CXX $f" $CXX -I"$rel_top_srcdir"/vendor $upx_submodule_defs -c "$f" done # echo "#==== link UPX =====" +test "x$obj_suffix" = "x" && obj_suffix=.o if test "x$AR" = "x"; then # link without using $AR - run "CXX upx" $CXX -o upx *.o */*.o + run "CXX upx" $CXX -o upx *${obj_suffix} */*${obj_suffix} else - run "AR libupx" $AR rcs ${AR_LIBFILE:-libupx_submodules.a} */*.o - run "CXX upx" $CXX -o upx *.o -L. -lupx_submodules + run "AR libupx" $AR rcs ${AR_LIBFILE:-libupx_submodules.a} */*${obj_suffix} + run "CXX upx" $CXX -o upx *${obj_suffix} -L. -lupx_submodules fi echo "# current directory: '$(pwd)'" ls -l upx* diff --git a/src/compress/compress.cpp b/src/compress/compress.cpp index be480d8c..6c23f664 100644 --- a/src/compress/compress.cpp +++ b/src/compress/compress.cpp @@ -62,7 +62,7 @@ unsigned upx_crc32(const void *buf, unsigned len, unsigned crc) **************************************************************************/ int upx_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len, - upx_callback_p cb, int method, int level, const upx_compress_config_t *cconf, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult) { int r = UPX_E_ERROR; upx_compress_result_t cresult_buffer; @@ -95,6 +95,10 @@ int upx_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned const unsigned orig_dst_len = *dst_len; if (__acc_cte(false)) { } +#if (WITH_BZIP2) + else if (M_IS_BZIP2(method)) + r = upx_bzip2_compress(src, src_len, dst, dst_len, cb, method, level, cconf, cresult); +#endif #if (WITH_LZMA) else if (M_IS_LZMA(method)) r = upx_lzma_compress(src, src_len, dst, dst_len, cb, method, level, cconf, cresult); @@ -112,7 +116,7 @@ int upx_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned r = upx_zstd_compress(src, src_len, dst, dst_len, cb, method, level, cconf, cresult); #endif else { - throwInternalError("unknown compression method"); + throwInternalError("unknown compression method %d", method); } #if 1 @@ -140,6 +144,10 @@ int upx_decompress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigne const unsigned orig_dst_len = *dst_len; if (__acc_cte(false)) { } +#if (WITH_BZIP2) + else if (M_IS_BZIP2(method)) + r = upx_bzip2_decompress(src, src_len, dst, dst_len, method, cresult); +#endif #if (WITH_LZMA) else if (M_IS_LZMA(method)) r = upx_lzma_decompress(src, src_len, dst, dst_len, method, cresult); @@ -161,7 +169,7 @@ int upx_decompress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigne r = upx_zstd_decompress(src, src_len, dst, dst_len, method, cresult); #endif else { - throwInternalError("unknown decompression method"); + throwInternalError("unknown compression method %d", method); } assert_noexcept(*dst_len <= orig_dst_len); @@ -187,6 +195,10 @@ int upx_test_overlap(const upx_bytep buf, const upx_bytep tbuf, unsigned src_off const unsigned orig_dst_len = *dst_len; if (__acc_cte(false)) { } +#if (WITH_BZIP2) + else if (M_IS_BZIP2(method)) + r = upx_bzip2_test_overlap(buf, tbuf, src_off, src_len, dst_len, method, cresult); +#endif #if (WITH_LZMA) else if (M_IS_LZMA(method)) r = upx_lzma_test_overlap(buf, tbuf, src_off, src_len, dst_len, method, cresult); @@ -204,7 +216,7 @@ int upx_test_overlap(const upx_bytep buf, const upx_bytep tbuf, unsigned src_off r = upx_zstd_test_overlap(buf, tbuf, src_off, src_len, dst_len, method, cresult); #endif else { - throwInternalError("unknown decompression method"); + throwInternalError("unknown compression method %d", method); } assert_noexcept(*dst_len <= orig_dst_len); diff --git a/src/compress/compress.h b/src/compress/compress.h index 9d4b74b2..41a881bc 100644 --- a/src/compress/compress.h +++ b/src/compress/compress.h @@ -36,19 +36,19 @@ int upx_bzip2_init(void); const char *upx_bzip2_version_string(void); int upx_bzip2_compress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, - upx_callback_p cb, + upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult ); int upx_bzip2_decompress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, + upx_bytep dst, unsigned *dst_len, int method, const upx_compress_result_t *cresult ); int upx_bzip2_test_overlap ( const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, unsigned src_len, - unsigned* dst_len, + unsigned *dst_len, int method, const upx_compress_result_t *cresult ); #endif @@ -57,19 +57,19 @@ int upx_bzip2_test_overlap ( const upx_bytep buf, int upx_lzma_init(void); const char *upx_lzma_version_string(void); int upx_lzma_compress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, - upx_callback_p cb, + upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult ); int upx_lzma_decompress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, + upx_bytep dst, unsigned *dst_len, int method, const upx_compress_result_t *cresult ); int upx_lzma_test_overlap ( const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, unsigned src_len, - unsigned* dst_len, + unsigned *dst_len, int method, const upx_compress_result_t *cresult ); #endif @@ -78,19 +78,19 @@ int upx_lzma_test_overlap ( const upx_bytep buf, int upx_nrv_init(void); const char *upx_nrv_version_string(void); int upx_nrv_compress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, - upx_callback_p cb, + upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult ); int upx_nrv_decompress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, + upx_bytep dst, unsigned *dst_len, int method, const upx_compress_result_t *cresult ); int upx_nrv_test_overlap ( const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, unsigned src_len, - unsigned* dst_len, + unsigned *dst_len, int method, const upx_compress_result_t *cresult ); #endif @@ -99,19 +99,19 @@ int upx_nrv_test_overlap ( const upx_bytep buf, int upx_ucl_init(void); const char *upx_ucl_version_string(void); int upx_ucl_compress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, - upx_callback_p cb, + upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult ); int upx_ucl_decompress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, + upx_bytep dst, unsigned *dst_len, int method, const upx_compress_result_t *cresult ); int upx_ucl_test_overlap ( const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, unsigned src_len, - unsigned* dst_len, + unsigned *dst_len, int method, const upx_compress_result_t *cresult ); unsigned upx_ucl_adler32(const void *buf, unsigned len, unsigned adler); @@ -122,19 +122,19 @@ unsigned upx_ucl_crc32 (const void *buf, unsigned len, unsigned crc); int upx_zlib_init(void); const char *upx_zlib_version_string(void); int upx_zlib_compress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, - upx_callback_p cb, + upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult ); int upx_zlib_decompress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, + upx_bytep dst, unsigned *dst_len, int method, const upx_compress_result_t *cresult ); int upx_zlib_test_overlap ( const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, unsigned src_len, - unsigned* dst_len, + unsigned *dst_len, int method, const upx_compress_result_t *cresult ); unsigned upx_zlib_adler32(const void *buf, unsigned len, unsigned adler); @@ -145,19 +145,19 @@ unsigned upx_zlib_crc32 (const void *buf, unsigned len, unsigned crc); int upx_zstd_init(void); const char *upx_zstd_version_string(void); int upx_zstd_compress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, - upx_callback_p cb, + upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult ); int upx_zstd_decompress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, + upx_bytep dst, unsigned *dst_len, int method, const upx_compress_result_t *cresult ); int upx_zstd_test_overlap ( const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, unsigned src_len, - unsigned* dst_len, + unsigned *dst_len, int method, const upx_compress_result_t *cresult ); #endif diff --git a/src/compress/compress_bzip2.cpp b/src/compress/compress_bzip2.cpp new file mode 100644 index 00000000..7581dc9a --- /dev/null +++ b/src/compress/compress_bzip2.cpp @@ -0,0 +1,208 @@ +/* compress_bzip2.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + */ + +#include "../conf.h" + +void bzip2_compress_config_t::reset() noexcept { mem_clear(this); } + +#if WITH_BZIP2 +#include "compress.h" +#include "../util/membuffer.h" +#include + +#if defined(BZ_NO_STDIO) || 1 +// we need to supply bz_internal_error() when building with BZ_NO_STDIO +extern "C" { +extern void bz_internal_error(int); +void bz_internal_error(int errcode) { throwInternalError("bz_internal_error %d", errcode); } +} +#endif // BZ_NO_STDIO + +static int convert_errno_from_bzip2(int r) { + switch (r) { + case BZ_OK: + return UPX_E_OK; + case BZ_MEM_ERROR: + return UPX_E_OUT_OF_MEMORY; + // TODO later: convert to UPX_E_INPUT_OVERRUN, UPX_E_OUTPUT_OVERRUN + default: + break; + } + return UPX_E_ERROR; +} + +/************************************************************************* +// +**************************************************************************/ + +int upx_bzip2_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb_parm, int method, int level, + const upx_compress_config_t *cconf_parm, upx_compress_result_t *cresult) { + assert(method == M_BZIP2); + assert(level > 0); + assert(cresult != nullptr); + UNUSED(cb_parm); + int r = UPX_E_ERROR; + const bzip2_compress_config_t *const lcconf = cconf_parm ? &cconf_parm->conf_bzip2 : nullptr; + bzip2_compress_result_t *const res = &cresult->result_bzip2; + res->reset(); + + int blockSize100k = (src_len + 100000 - 1) / 100000; + if (blockSize100k < 1) + blockSize100k = 1; + if (blockSize100k > 9) + blockSize100k = 9; + // idea for later: could enhance lcconf to allow setting blockSize100k + UNUSED(lcconf); + if (level <= 3 && blockSize100k > level) + blockSize100k = level; + + char *dest = (char *) dst; + char *source = (char *) const_cast(src); + r = BZ2_bzBuffToBuffCompress(dest, dst_len, source, src_len, blockSize100k, 0, 0); + return convert_errno_from_bzip2(r); +} + +/************************************************************************* +// +**************************************************************************/ + +int upx_bzip2_decompress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len, + int method, const upx_compress_result_t *cresult) { + assert(method == M_BZIP2); + UNUSED(method); + UNUSED(cresult); + char *dest = (char *) dst; + char *source = (char *) const_cast(src); + int small = 0; + int r = BZ2_bzBuffToBuffDecompress(dest, dst_len, source, src_len, small, 0); + return convert_errno_from_bzip2(r); +} + +/************************************************************************* +// test_overlap - see for semantics +**************************************************************************/ + +int upx_bzip2_test_overlap(const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, + unsigned src_len, unsigned *dst_len, int method, + const upx_compress_result_t *cresult) { + assert(method == M_BZIP2); + + MemBuffer b(src_off + src_len); + memcpy(b + src_off, buf + src_off, src_len); + unsigned saved_dst_len = *dst_len; + int r = upx_bzip2_decompress(raw_index_bytes(b, src_off, src_len), src_len, + raw_bytes(b, *dst_len), dst_len, method, cresult); + if (r != UPX_E_OK) + return r; + if (*dst_len != saved_dst_len) + return UPX_E_ERROR; + // NOTE: there is a very tiny possibility that decompression has + // succeeded but the data is not restored correctly because of + // in-place buffer overlapping, so we use an extra memcmp(). + if (tbuf != nullptr && memcmp(tbuf, b, *dst_len) != 0) + return UPX_E_ERROR; + return UPX_E_OK; +} + +/************************************************************************* +// misc +**************************************************************************/ + +int upx_bzip2_init(void) { return 0; } + +const char *upx_bzip2_version_string(void) { return BZ2_bzlibVersion(); } + +/************************************************************************* +// doctest checks +**************************************************************************/ + +#if DEBUG && !defined(DOCTEST_CONFIG_DISABLE) && 1 + +static bool check_bzip2(const int method, const int level, const unsigned expected_c_len) { + const unsigned u_len = 16384; + const unsigned c_extra = 4096; + MemBuffer u_buf, c_buf, d_buf; + unsigned c_len, d_len; + upx_compress_result_t cresult; + int r; + + u_buf.alloc(u_len); + memset(u_buf, 0, u_len); + c_buf.allocForCompression(u_len, c_extra); + d_buf.allocForDecompression(u_len); + + c_len = c_buf.getSize() - c_extra; + r = upx_bzip2_compress(raw_bytes(u_buf, u_len), u_len, raw_index_bytes(c_buf, c_extra, c_len), + &c_len, nullptr, method, level, NULL_cconf, &cresult); + if (r != 0 || c_len != expected_c_len) + return false; + + d_len = d_buf.getSize(); + r = upx_bzip2_decompress(raw_index_bytes(c_buf, c_extra, c_len), c_len, raw_bytes(d_buf, d_len), + &d_len, method, nullptr); + if (r != 0 || d_len != u_len || memcmp(u_buf, d_buf, u_len) != 0) + return false; + + d_len = u_len - 1; + r = upx_bzip2_decompress(raw_index_bytes(c_buf, c_extra, c_len), c_len, raw_bytes(d_buf, d_len), + &d_len, method, nullptr); + if (r == 0) + return false; + + // TODO: rewrite Packer::findOverlapOverhead() so that we can test it here + // unsigned x_len = d_len; + // r = upx_bzip2_test_overlap(c_buf, u_buf, c_extra, c_len, &x_len, method, nullptr); + return true; +} + +TEST_CASE("compress_bzip2") { CHECK(check_bzip2(M_BZIP2, 9, 46)); } + +#endif // DEBUG + +TEST_CASE("upx_bzip2_decompress") { +#if 0 // TODO later, see above + const byte *c_data; + byte d_buf[32]; + unsigned d_len; + int r; + + c_data = (const byte *) "\x28\xb5\x2f\xfd\x20\x20\x3d\x00\x00\x08\xff\x01\x00\x34\x4e\x08"; + d_len = 32; + r = upx_bzip2_decompress(c_data, 16, d_buf, &d_len, M_BZIP2, nullptr); + CHECK((r == 0 && d_len == 32)); + r = upx_bzip2_decompress(c_data, 15, d_buf, &d_len, M_BZIP2, nullptr); + CHECK(r == UPX_E_INPUT_OVERRUN); + d_len = 31; + r = upx_bzip2_decompress(c_data, 16, d_buf, &d_len, M_BZIP2, nullptr); + CHECK(r == UPX_E_OUTPUT_OVERRUN); + UNUSED(r); +#endif // TODO +} + +#endif // WITH_BZIP2 + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/compress/compress_lzma.cpp b/src/compress/compress_lzma.cpp index 03157d5c..80388251 100644 --- a/src/compress/compress_lzma.cpp +++ b/src/compress/compress_lzma.cpp @@ -293,7 +293,7 @@ struct ProgressInfo final : public ICompressProgressInfo, public CMyUnknownImp { virtual ~ProgressInfo() {} MY_UNKNOWN_IMP STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize) override; - upx_callback_p cb = nullptr; + upx_callback_t *cb = nullptr; }; STDMETHODIMP ProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { @@ -317,7 +317,7 @@ STDMETHODIMP ProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outS #undef RC_NORMALIZE int upx_lzma_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len, - upx_callback_p cb, int method, int level, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf_parm, upx_compress_result_t *cresult) { assert(M_IS_LZMA(method)); assert(level > 0); @@ -327,6 +327,7 @@ int upx_lzma_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsi HRESULT rh; const lzma_compress_config_t *const lcconf = cconf_parm ? &cconf_parm->conf_lzma : nullptr; lzma_compress_result_t *const res = &cresult->result_lzma; + res->reset(); MyLzma::InStream is; is.AddRef(); diff --git a/src/compress/compress_ucl.cpp b/src/compress/compress_ucl.cpp index d1b413e2..44500f9b 100644 --- a/src/compress/compress_ucl.cpp +++ b/src/compress/compress_ucl.cpp @@ -66,7 +66,7 @@ extern "C" { static void __UCL_CDECL wrap_nprogress_ucl(ucl_uint a, ucl_uint b, int state, ucl_voidp user) { if (state != -1 && state != 3) return; - upx_callback_p cb = (upx_callback_p) user; + upx_callback_t *cb = (upx_callback_t *) user; if (cb && cb->nprogress) cb->nprogress(cb, a, b); } @@ -77,7 +77,7 @@ static void __UCL_CDECL wrap_nprogress_ucl(ucl_uint a, ucl_uint b, int state, uc **************************************************************************/ int upx_ucl_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len, - upx_callback_p cb_parm, int method, int level, + upx_callback_t *cb_parm, int method, int level, const upx_compress_config_t *cconf_parm, upx_compress_result_t *cresult) { int r; assert(level > 0); diff --git a/src/compress/compress_zlib.cpp b/src/compress/compress_zlib.cpp index 0c5aed75..b9a89c52 100644 --- a/src/compress/compress_zlib.cpp +++ b/src/compress/compress_zlib.cpp @@ -75,7 +75,7 @@ static int convert_errno_from_zlib(int zr) { **************************************************************************/ int upx_zlib_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len, - upx_callback_p cb_parm, int method, int level, + upx_callback_t *cb_parm, int method, int level, const upx_compress_config_t *cconf_parm, upx_compress_result_t *cresult) { assert(method == M_DEFLATE); assert(level > 0); @@ -85,6 +85,7 @@ int upx_zlib_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsi int zr; const zlib_compress_config_t *const lcconf = cconf_parm ? &cconf_parm->conf_zlib : nullptr; zlib_compress_result_t *const res = &cresult->result_zlib; + res->reset(); if (level == 10) level = 9; @@ -99,8 +100,6 @@ int upx_zlib_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsi oassign(strategy, lcconf->strategy); } - res->dummy = 0; - z_stream s; s.zalloc = (alloc_func) nullptr; s.zfree = (free_func) nullptr; diff --git a/src/compress/compress_zstd.cpp b/src/compress/compress_zstd.cpp index 2f585e4d..a00841c5 100644 --- a/src/compress/compress_zstd.cpp +++ b/src/compress/compress_zstd.cpp @@ -55,7 +55,7 @@ static int convert_errno_from_zstd(size_t zr) { **************************************************************************/ int upx_zstd_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len, - upx_callback_p cb_parm, int method, int level, + upx_callback_t *cb_parm, int method, int level, const upx_compress_config_t *cconf_parm, upx_compress_result_t *cresult) { assert(method == M_ZSTD); assert(level > 0); @@ -65,6 +65,7 @@ int upx_zstd_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsi size_t zr; const zstd_compress_config_t *const lcconf = cconf_parm ? &cconf_parm->conf_zstd : nullptr; zstd_compress_result_t *const res = &cresult->result_zstd; + res->reset(); // TODO later: map level 1..10 to zstd-level 1..22 if (level == 10) @@ -75,8 +76,6 @@ int upx_zstd_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsi UNUSED(lcconf); } - res->dummy = 0; - zr = ZSTD_compress(dst, *dst_len, src, src_len, level); if (ZSTD_isError(zr)) { *dst_len = 0; // TODO ??? diff --git a/src/conf.h b/src/conf.h index a25d9209..3802d206 100644 --- a/src/conf.h +++ b/src/conf.h @@ -154,7 +154,7 @@ typedef unsigned char uchar; // convention: use "charptr" when dealing with abstract pointer arithmetics #define charptr upx_charptr_unit_type * // upx_charptr_unit_type is some opaque type with sizeof(type) == 1 -struct alignas(1) upx_charptr_unit_type { char hidden__; }; +struct alignas(1) upx_charptr_unit_type final { char hidden__; }; ACC_COMPILE_TIME_ASSERT_HEADER(sizeof(upx_charptr_unit_type) == 1) // using the system off_t was a bad idea even back in 199x... @@ -576,6 +576,7 @@ using upx::tribool; #define M_LZMA 14 #define M_DEFLATE 15 // zlib #define M_ZSTD 16 +#define M_BZIP2 17 // compression methods internal usage #define M_ALL (-1) #define M_END (-2) @@ -590,6 +591,7 @@ using upx::tribool; #define M_IS_LZMA(x) (((x) &255) == M_LZMA) #define M_IS_DEFLATE(x) ((x) == M_DEFLATE) #define M_IS_ZSTD(x) ((x) == M_ZSTD) +#define M_IS_BZIP2(x) ((x) == M_BZIP2) // filters internal usage #define FT_END (-1) @@ -613,10 +615,9 @@ using upx::tribool; #endif struct upx_callback_t; -typedef upx_callback_t *upx_callback_p; -typedef void(__acc_cdecl *upx_progress_func_t)(upx_callback_p, unsigned, unsigned); +typedef void(__acc_cdecl *upx_progress_func_t)(upx_callback_t *, unsigned, unsigned); -struct upx_callback_t { +struct upx_callback_t final { upx_progress_func_t nprogress; void *user; @@ -627,7 +628,13 @@ struct upx_callback_t { // compression - config_t **************************************************************************/ -struct lzma_compress_config_t { +struct bzip2_compress_config_t final { + unsigned dummy; + + void reset() noexcept; +}; + +struct lzma_compress_config_t final { typedef OptVar pos_bits_t; // pb typedef OptVar lit_pos_bits_t; // lp typedef OptVar lit_context_bits_t; // lc @@ -647,11 +654,11 @@ struct lzma_compress_config_t { void reset() noexcept; }; -struct ucl_compress_config_t : public REAL_ucl_compress_config_t { +struct ucl_compress_config_t final : public REAL_ucl_compress_config_t { void reset() noexcept { memset(this, 0xff, sizeof(*this)); } }; -struct zlib_compress_config_t { +struct zlib_compress_config_t final { typedef OptVar mem_level_t; // ml typedef OptVar window_bits_t; // wb typedef OptVar strategy_t; // st @@ -663,19 +670,21 @@ struct zlib_compress_config_t { void reset() noexcept; }; -struct zstd_compress_config_t { +struct zstd_compress_config_t final { unsigned dummy; void reset() noexcept; }; -struct upx_compress_config_t { +struct upx_compress_config_t final { + bzip2_compress_config_t conf_bzip2; lzma_compress_config_t conf_lzma; ucl_compress_config_t conf_ucl; zlib_compress_config_t conf_zlib; zstd_compress_config_t conf_zstd; void reset() noexcept { + conf_bzip2.reset(); conf_lzma.reset(); conf_ucl.reset(); conf_zlib.reset(); @@ -689,7 +698,13 @@ struct upx_compress_config_t { // compression - result_t **************************************************************************/ -struct lzma_compress_result_t { +struct bzip2_compress_result_t final { + unsigned dummy; + + void reset() noexcept { mem_clear(this); } +}; + +struct lzma_compress_result_t final { unsigned pos_bits; // pb unsigned lit_pos_bits; // lp unsigned lit_context_bits; // lc @@ -702,25 +717,25 @@ struct lzma_compress_result_t { void reset() noexcept { mem_clear(this); } }; -struct ucl_compress_result_t { +struct ucl_compress_result_t final { ucl_uint result[16]; void reset() noexcept { mem_clear(this); } }; -struct zlib_compress_result_t { +struct zlib_compress_result_t final { unsigned dummy; void reset() noexcept { mem_clear(this); } }; -struct zstd_compress_result_t { +struct zstd_compress_result_t final { unsigned dummy; void reset() noexcept { mem_clear(this); } }; -struct upx_compress_result_t { +struct upx_compress_result_t final { // debugging aid struct Debug { int method, level; @@ -729,6 +744,7 @@ struct upx_compress_result_t { }; Debug debug; + bzip2_compress_result_t result_bzip2; lzma_compress_result_t result_lzma; ucl_compress_result_t result_ucl; zlib_compress_result_t result_zlib; @@ -736,6 +752,7 @@ struct upx_compress_result_t { void reset() noexcept { debug.reset(); + result_bzip2.reset(); result_lzma.reset(); result_ucl.reset(); result_zlib.reset(); @@ -802,19 +819,19 @@ unsigned upx_adler32(const void *buf, unsigned len, unsigned adler = 1); unsigned upx_crc32 (const void *buf, unsigned len, unsigned crc = 0); int upx_compress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, - upx_callback_p cb, + upx_bytep dst, unsigned *dst_len, + upx_callback_t *cb, int method, int level, const upx_compress_config_t *cconf, upx_compress_result_t *cresult ); int upx_decompress ( const upx_bytep src, unsigned src_len, - upx_bytep dst, unsigned* dst_len, + upx_bytep dst, unsigned *dst_len, int method, const upx_compress_result_t *cresult ); int upx_test_overlap ( const upx_bytep buf, const upx_bytep tbuf, unsigned src_off, unsigned src_len, - unsigned* dst_len, + unsigned *dst_len, int method, const upx_compress_result_t *cresult ); // clang-format on diff --git a/src/except.cpp b/src/except.cpp index c0ca7190..7df91db5 100644 --- a/src/except.cpp +++ b/src/except.cpp @@ -150,7 +150,7 @@ void throwCantPack(const char *format, ...) { char msg[1024]; va_list ap; va_start(ap, format); - (void) upx_safe_vsnprintf(msg, sizeof(msg), format, ap); + (void) upx_safe_vsnprintf_noexcept(msg, sizeof(msg), format, ap); va_end(ap); throwCantPack(msg); } @@ -160,7 +160,17 @@ void throwCantUnpack(const char *format, ...) { char msg[1024]; va_list ap; va_start(ap, format); - (void) upx_safe_vsnprintf(msg, sizeof(msg), format, ap); + (void) upx_safe_vsnprintf_noexcept(msg, sizeof(msg), format, ap); + va_end(ap); + throwCantUnpack(msg); +} + +template <> +void throwInternalError(const char *format, ...) { + char msg[1024]; + va_list ap; + va_start(ap, format); + (void) upx_safe_vsnprintf_noexcept(msg, sizeof(msg), format, ap); va_end(ap); throwCantUnpack(msg); } diff --git a/src/except.h b/src/except.h index ac7fcd42..3669bb89 100644 --- a/src/except.h +++ b/src/except.h @@ -212,6 +212,10 @@ template void throwCantUnpack(const T *, ...) DELETED_FUNCTION; template <> NORET void throwCantUnpack(const char *format, ...) may_throw attribute_format(1, 2); +template +void throwInternalError(const T *, ...) DELETED_FUNCTION; +template <> +NORET void throwInternalError(const char *format, ...) may_throw attribute_format(1, 2); #undef NORET diff --git a/src/help.cpp b/src/help.cpp index b847869d..c6622f1c 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -411,6 +411,11 @@ void show_version(bool one_line) { if (v != nullptr && v[0]) fprintf(f, "zstd data compression library %s\n", v); #endif +#if (WITH_BZIP2) + v = upx_bzip2_version_string(); + if (v != nullptr && v[0]) + fprintf(f, "bzip2 data compression library %s\n", v); +#endif #if !defined(DOCTEST_CONFIG_DISABLE) fprintf(f, "doctest C++ testing framework version %s\n", DOCTEST_VERSION_STR); #endif @@ -429,6 +434,9 @@ void show_version(bool one_line) { // see vendor/zstd/LICENSE; main author is Yann Collet fprintf(f, "Copyright (C) 2015" "-2023 Meta Platforms, Inc. and affiliates\n"); #endif +#if (WITH_BZIP2) + fprintf(f, "Copyright (C) 1996" "-2010 Julian Seward\n"); // see +#endif #if !defined(DOCTEST_CONFIG_DISABLE) fprintf(f, "Copyright (C) 2016" "-2023 Viktor Kirilov\n"); #endif diff --git a/src/pefile.cpp b/src/pefile.cpp index 176ba812..5bb723c0 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -2928,7 +2928,7 @@ void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, unsigned take = sizeof(oh); SPAN_S_VAR(byte, extra_info, obuf); extra_info = obuf.subref("bad extra_info offset %#x", skip, take); - // byte * const eistart = raw_bytes(extra_info, 0); + // byte *const eistart = raw_bytes(extra_info, 0); memcpy(&oh, extra_info, take); extra_info += take; diff --git a/src/ui.cpp b/src/ui.cpp index 8cd008ec..ce47c85c 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -354,7 +354,7 @@ void UiPacker::endCallback(bool done) { **************************************************************************/ /*static*/ -void __acc_cdecl UiPacker::progress_callback(upx_callback_p cb, unsigned isize, unsigned osize) { +void __acc_cdecl UiPacker::progress_callback(upx_callback_t *cb, unsigned isize, unsigned osize) { // printf("%6d %6d %d\n", isize, osize, state); UiPacker *self = (UiPacker *) cb->user; self->doCallback(isize, osize); diff --git a/src/ui.h b/src/ui.h index f65b8103..a117c0c3 100644 --- a/src/ui.h +++ b/src/ui.h @@ -69,7 +69,7 @@ public: virtual upx_callback_t *getCallback() { return &cb; } protected: - static void __acc_cdecl progress_callback(upx_callback_p cb, unsigned, unsigned); + static void __acc_cdecl progress_callback(upx_callback_t *, unsigned, unsigned); virtual void doCallback(unsigned isize, unsigned osize); protected: diff --git a/src/util/snprintf.cpp b/src/util/snprintf.cpp index 01b92621..6194857d 100644 --- a/src/util/snprintf.cpp +++ b/src/util/snprintf.cpp @@ -40,6 +40,15 @@ upx_rsize_t upx_safe_strlen(const char *s) { #define strlen upx_safe_strlen } +upx_rsize_t upx_safe_strlen_noexcept(const char *s) noexcept { +#undef strlen + assert_noexcept(s != nullptr); + size_t len = strlen(s); + assert_noexcept(len < UPX_RSIZE_MAX_STR); + return len; +#define strlen upx_safe_strlen +} + int upx_safe_vsnprintf(char *str, upx_rsize_t max_size, const char *format, va_list ap) { #undef vsnprintf size_t size; @@ -68,6 +77,35 @@ int upx_safe_vsnprintf(char *str, upx_rsize_t max_size, const char *format, va_l #define vsnprintf upx_safe_vsnprintf } +int upx_safe_vsnprintf_noexcept(char *str, upx_rsize_t max_size, const char *format, + va_list ap) noexcept { +#undef vsnprintf + size_t size; + + // preconditions + assert_noexcept(max_size <= UPX_RSIZE_MAX_STR); + if (str != nullptr) + assert_noexcept(max_size > 0); + else + assert_noexcept(max_size == 0); + + long long len = vsnprintf(str, max_size, format, ap); + assert_noexcept(len >= 0); + assert_noexcept(len < UPX_RSIZE_MAX_STR); + size = (size_t) len + 1; + + // postconditions + assert_noexcept(size > 0); + assert_noexcept(size <= UPX_RSIZE_MAX_STR); + if (str != nullptr) { + assert_noexcept(size <= max_size); + assert_noexcept(str[size - 1] == '\0'); + } + + return ACC_ICONV(int, size - 1); // snprintf() returns length, not size +#define vsnprintf upx_safe_vsnprintf +} + int upx_safe_snprintf(char *str, upx_rsize_t max_size, const char *format, ...) { va_list ap; int len; diff --git a/src/util/snprintf.h b/src/util/snprintf.h index 3d738faf..8ca5635d 100644 --- a/src/util/snprintf.h +++ b/src/util/snprintf.h @@ -46,6 +46,11 @@ char *upx_safe_xprintf(const char *format, ...) attribute_format(1, 2); upx_rsize_t upx_safe_strlen(const char *); +// noexcept variants (these use "assert_noexcept") +int upx_safe_vsnprintf_noexcept(char *str, upx_rsize_t max_size, const char *format, + va_list ap) noexcept; +upx_rsize_t upx_safe_strlen_noexcept(const char *) noexcept; + // globally redirect some functions #undef strlen #define strlen upx_safe_strlen