src: add a check for libc qsort(); cleanups

This commit is contained in:
Markus F.X.J. Oberhumer 2023-09-01 11:59:29 +02:00
parent 3c4b959f78
commit 0ac6c36af2
11 changed files with 89 additions and 44 deletions

View File

@ -12,8 +12,8 @@ env:
CMAKE_REQUIRED_QUIET: OFF CMAKE_REQUIRED_QUIET: OFF
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
UPX_CMAKE_BUILD_FLAGS: --verbose UPX_CMAKE_BUILD_FLAGS: --verbose
# 2023-08-26 # 2023-08-31
ZIG_DIST_VERSION: 0.12.0-dev.170+750998eef ZIG_DIST_VERSION: 0.12.0-dev.244+f4c9e19bc
jobs: jobs:
job-rebuild-and-verify-stubs: job-rebuild-and-verify-stubs:

View File

@ -10,8 +10,8 @@ on:
env: env:
CMAKE_REQUIRED_QUIET: OFF CMAKE_REQUIRED_QUIET: OFF
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
# 2023-08-26 # 2023-08-31
ZIG_DIST_VERSION: 0.12.0-dev.170+750998eef ZIG_DIST_VERSION: 0.12.0-dev.244+f4c9e19bc
jobs: jobs:
job-linux-zigcc: # uses cmake + make job-linux-zigcc: # uses cmake + make

View File

@ -103,6 +103,7 @@ all_errors=
export UPX="--prefer-ucl --no-color --no-progress" export UPX="--prefer-ucl --no-color --no-progress"
export UPX_DEBUG_DISABLE_GITREV_WARNING=1 export UPX_DEBUG_DISABLE_GITREV_WARNING=1
export UPX_DEBUG_DOCTEST_VERBOSE=0 export UPX_DEBUG_DOCTEST_VERBOSE=0
export NO_COLOR=1
rm -rf ./testsuite_1 rm -rf ./testsuite_1
mkdir testsuite_1 || exit 1 mkdir testsuite_1 || exit 1

View File

@ -69,7 +69,7 @@ struct LE32;
struct LE64; struct LE64;
// Note: // Note:
// void is explicitly allowed, but there is no automatic pointer conversion because of template // void is explicitly allowed (but there is no automatic pointer conversion because of template!)
// char is explicitly allowed // char is explicitly allowed
// byte is explicitly allowed // byte is explicitly allowed
template <class T> template <class T>
@ -161,13 +161,19 @@ static forceinline upx_uint64_t bswap64(upx_uint64_t v) noexcept { return _bytes
#else #else
static forceinline constexpr unsigned bswap16(unsigned v) noexcept { static forceinline constexpr unsigned bswap16(unsigned v) noexcept {
#if defined(__riscv) && __riscv_xlen == 64
return (unsigned) __builtin_bswap64((upx_uint64_t) v << 48);
#else
// return __builtin_bswap16((upx_uint16_t) (v & 0xffff)); // return __builtin_bswap16((upx_uint16_t) (v & 0xffff));
// return (unsigned) __builtin_bswap64((upx_uint64_t) v << 48);
return __builtin_bswap32(v << 16); return __builtin_bswap32(v << 16);
#endif
} }
static forceinline constexpr unsigned bswap32(unsigned v) noexcept { static forceinline constexpr unsigned bswap32(unsigned v) noexcept {
// return (unsigned) __builtin_bswap64((upx_uint64_t) v << 32); #if defined(__riscv) && __riscv_xlen == 64
return (unsigned) __builtin_bswap64((upx_uint64_t) v << 32);
#else
return __builtin_bswap32(v); return __builtin_bswap32(v);
#endif
} }
static forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) noexcept { static forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) noexcept {
return __builtin_bswap64(v); return __builtin_bswap64(v);

View File

@ -43,6 +43,8 @@ int upx_doctest_check(int argc, char **argv) {
return 0; return 0;
#else #else
const char *e = getenv("UPX_DEBUG_DOCTEST_DISABLE"); const char *e = getenv("UPX_DEBUG_DOCTEST_DISABLE");
if (!e)
e = getenv("UPX_DEBUG_DISABLE_DOCTEST"); // allow alternate spelling
if (e && e[0] && strcmp(e, "0") != 0) if (e && e[0] && strcmp(e, "0") != 0)
return 0; return 0;
bool minimal = true; // don't show summary bool minimal = true; // don't show summary
@ -319,7 +321,7 @@ struct TestIntegerWrap {
static inline bool neg_eq(const T x) noexcept { return T(0) - x == x; } static inline bool neg_eq(const T x) noexcept { return T(0) - x == x; }
}; };
static noinline void throwSomeValue(int x) { static noinline void throwSomeValue(int x) may_throw {
if (x < 0) if (x < 0)
throw int(x); throw int(x);
else else
@ -346,6 +348,8 @@ static noinline void check_basic_cxx_exception_handling(void (*func)(int)) noexc
void upx_compiler_sanity_check(void) noexcept { void upx_compiler_sanity_check(void) noexcept {
const char *e = getenv("UPX_DEBUG_DOCTEST_DISABLE"); const char *e = getenv("UPX_DEBUG_DOCTEST_DISABLE");
if (!e)
e = getenv("UPX_DEBUG_DISABLE_DOCTEST"); // allow alternate spelling
if (e && e[0] && strcmp(e, "0") != 0) { if (e && e[0] && strcmp(e, "0") != 0) {
// If UPX_DEBUG_DOCTEST_DISABLE is set then we don't want to throw any // If UPX_DEBUG_DOCTEST_DISABLE is set then we don't want to throw any
// exceptions in order to improve debugging experience. // exceptions in order to improve debugging experience.
@ -622,4 +626,36 @@ TEST_CASE("libc snprintf") {
CHECK_EQ(strcmp(buf, "-7.0.0.0.0.0.0.0.7.0xffffffffffffffff"), 0); CHECK_EQ(strcmp(buf, "-7.0.0.0.0.0.0.0.7.0xffffffffffffffff"), 0);
} }
TEST_CASE("libc qsort") {
// runtime check that libc qsort() never compares identical objects
struct Elem {
upx_uint16_t id;
upx_uint16_t value;
static int compare(const void *aa, const void *bb) noexcept {
const Elem *a = (const Elem *) aa;
const Elem *b = (const Elem *) bb;
assert_noexcept(a->id != b->id); // check not IDENTICAL
return a->value < b->value ? -1 : (a->value == b->value ? 0 : 1);
}
static bool qsort_check(Elem *e, size_t n) {
upx_uint32_t x = n + 5381;
for (size_t i = 0; i < n; i++) {
e[i].id = (upx_uint16_t) i;
x = x * 33 + (i & 255);
e[i].value = (upx_uint16_t) x;
}
qsort(e, n, sizeof(*e), Elem::compare);
bool sorted_ok = true;
for (size_t i = 1; i < n; i++)
sorted_ok &= e[i - 1].value <= e[i].value;
return sorted_ok;
}
};
constexpr size_t N = 4096;
Elem e[N];
for (size_t n = 1; n <= N; n <<= 1)
CHECK(Elem::qsort_check(e, n));
}
/* vim:set ts=4 sw=4 et: */ /* vim:set ts=4 sw=4 et: */

View File

@ -186,32 +186,32 @@ public:
#define NORET noinline #define NORET noinline
#endif #endif
NORET void throwCantPack(const char *msg); NORET void throwCantPack(const char *msg) may_throw;
NORET void throwCantPackExact(); NORET void throwCantPackExact() may_throw;
NORET void throwUnknownExecutableFormat(const char *msg = nullptr, bool warn = false); NORET void throwUnknownExecutableFormat(const char *msg = nullptr, bool warn = false) may_throw;
NORET void throwNotCompressible(const char *msg = nullptr); NORET void throwNotCompressible(const char *msg = nullptr) may_throw;
NORET void throwAlreadyPacked(const char *msg = nullptr); NORET void throwAlreadyPacked(const char *msg = nullptr) may_throw;
NORET void throwAlreadyPackedByUPX(const char *msg = nullptr); NORET void throwAlreadyPackedByUPX(const char *msg = nullptr) may_throw;
NORET void throwCantUnpack(const char *msg); NORET void throwCantUnpack(const char *msg) may_throw;
NORET void throwNotPacked(const char *msg = nullptr); NORET void throwNotPacked(const char *msg = nullptr) may_throw;
NORET void throwFilterException(); NORET void throwFilterException() may_throw;
NORET void throwBadLoader(); NORET void throwBadLoader() may_throw;
NORET void throwChecksumError(); NORET void throwChecksumError() may_throw;
NORET void throwCompressedDataViolation(); NORET void throwCompressedDataViolation() may_throw;
NORET void throwInternalError(const char *msg); NORET void throwInternalError(const char *msg) may_throw;
NORET void throwOutOfMemoryException(const char *msg = nullptr); NORET void throwOutOfMemoryException(const char *msg = nullptr) may_throw;
NORET void throwIOException(const char *msg = nullptr, int e = 0); NORET void throwIOException(const char *msg = nullptr, int e = 0) may_throw;
NORET void throwEOFException(const char *msg = nullptr, int e = 0); NORET void throwEOFException(const char *msg = nullptr, int e = 0) may_throw;
// some C++ template wizardry is needed to overload throwCantPack() for varargs // some C++ template wizardry is needed to overload throwCantPack() for varargs
template <class T> template <class T>
void throwCantPack(const T *, ...) DELETED_FUNCTION; void throwCantPack(const T *, ...) DELETED_FUNCTION;
template <> template <>
NORET void throwCantPack(const char *format, ...) attribute_format(1, 2); NORET void throwCantPack(const char *format, ...) may_throw attribute_format(1, 2);
template <class T> template <class T>
void throwCantUnpack(const T *, ...) DELETED_FUNCTION; void throwCantUnpack(const T *, ...) DELETED_FUNCTION;
template <> template <>
NORET void throwCantUnpack(const char *format, ...) attribute_format(1, 2); NORET void throwCantUnpack(const char *format, ...) may_throw attribute_format(1, 2);
#undef NORET #undef NORET

View File

@ -1238,8 +1238,9 @@ Linker* PackVmlinuxAMD64::newLinker() const
// instantiate instances // instantiate instances
template class PackVmlinuxBase<ElfClass_LE32>;
template class PackVmlinuxBase<ElfClass_BE32>; template class PackVmlinuxBase<ElfClass_BE32>;
// template class PackVmlinuxBase<ElfClass_BE64>; // not used
template class PackVmlinuxBase<ElfClass_LE32>;
template class PackVmlinuxBase<ElfClass_LE64>; template class PackVmlinuxBase<ElfClass_LE64>;
/* vim:set ts=4 sw=4 et: */ /* vim:set ts=4 sw=4 et: */

View File

@ -91,7 +91,7 @@ PackMaster::~PackMaster() noexcept {
// //
**************************************************************************/ **************************************************************************/
static tribool try_can_pack(PackerBase *pb, void *user) may_throw { static noinline tribool try_can_pack(PackerBase *pb, void *user) may_throw {
InputFile *f = (InputFile *) user; InputFile *f = (InputFile *) user;
try { try {
pb->initPackHeader(); pb->initPackHeader();
@ -111,7 +111,7 @@ static tribool try_can_pack(PackerBase *pb, void *user) may_throw {
return false; return false;
} }
static tribool try_can_unpack(PackerBase *pb, void *user) may_throw { static noinline tribool try_can_unpack(PackerBase *pb, void *user) may_throw {
InputFile *f = (InputFile *) user; InputFile *f = (InputFile *) user;
try { try {
pb->initPackHeader(); pb->initPackHeader();

View File

@ -40,11 +40,11 @@ public:
explicit PackMaster(InputFile *f, Options *o = nullptr) noexcept; explicit PackMaster(InputFile *f, Options *o = nullptr) noexcept;
~PackMaster() noexcept; ~PackMaster() noexcept;
void pack(OutputFile *fo); void pack(OutputFile *fo) may_throw;
void unpack(OutputFile *fo); void unpack(OutputFile *fo) may_throw;
void test(); void test() may_throw;
void list(); void list() may_throw;
void fileInfo(); void fileInfo() may_throw;
typedef tribool (*visit_func_t)(PackerBase *pb, void *user); typedef tribool (*visit_func_t)(PackerBase *pb, void *user);
static PackerBase *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user) static PackerBase *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user)
@ -54,8 +54,8 @@ private:
OwningPointer(PackerBase) packer = nullptr; // owner OwningPointer(PackerBase) packer = nullptr; // owner
InputFile *const fi; // reference, required InputFile *const fi; // reference, required
static PackerBase *getPacker(InputFile *f); static PackerBase *getPacker(InputFile *f) may_throw;
static PackerBase *getUnpacker(InputFile *f); static PackerBase *getUnpacker(InputFile *f) may_throw;
// setup local options for each file // setup local options for each file
Options local_options; Options local_options;

View File

@ -704,8 +704,9 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 {
if (rc != 0) if (rc != 0)
return rc; return rc;
// What could remain? // What could remain?
// make sort order deterministic
assert_noexcept(a->sort_id != b->sort_id); assert_noexcept(a->sort_id != b->sort_id);
return a->sort_id < b->sort_id ? -1 : 1; // make sort order deterministic return a->sort_id < b->sort_id ? -1 : 1;
} }
virtual void alignCode(unsigned len) override { alignWithByte(len, 0); } virtual void alignCode(unsigned len) override { alignWithByte(len, 0); }

View File

@ -42,13 +42,13 @@
XSPAN_NAMESPACE_BEGIN XSPAN_NAMESPACE_BEGIN
// HINT: set env-var "UPX_DEBUG_DOCTEST_DISABLE=1" for improved debugging experience // HINT: set env-var "UPX_DEBUG_DOCTEST_DISABLE=1" for improved debugging experience
noinline void xspan_fail_nullptr(void); noinline void xspan_fail_nullptr(void) may_throw;
noinline void xspan_fail_nullbase(void); noinline void xspan_fail_nullbase(void) may_throw;
noinline void xspan_fail_not_same_base(void); noinline void xspan_fail_not_same_base(void) may_throw;
noinline void xspan_fail_range_nullptr(void); noinline void xspan_fail_range_nullptr(void) may_throw;
noinline void xspan_fail_range_nullbase(void); noinline void xspan_fail_range_nullbase(void) may_throw;
noinline void xspan_fail_range_range(void); noinline void xspan_fail_range_range(void) may_throw;
void xspan_check_range(const void *ptr, const void *base, ptrdiff_t size_in_bytes); void xspan_check_range(const void *ptr, const void *base, ptrdiff_t size_in_bytes) may_throw;
// help constructor to distinguish between number of elements and bytes // help constructor to distinguish between number of elements and bytes
struct XSpanCount { struct XSpanCount {