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
DEBIAN_FRONTEND: noninteractive
UPX_CMAKE_BUILD_FLAGS: --verbose
# 2023-08-26
ZIG_DIST_VERSION: 0.12.0-dev.170+750998eef
# 2023-08-31
ZIG_DIST_VERSION: 0.12.0-dev.244+f4c9e19bc
jobs:
job-rebuild-and-verify-stubs:

View File

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

View File

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

View File

@ -69,7 +69,7 @@ struct LE32;
struct LE64;
// 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
// byte is explicitly allowed
template <class T>
@ -161,13 +161,19 @@ static forceinline upx_uint64_t bswap64(upx_uint64_t v) noexcept { return _bytes
#else
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 (unsigned) __builtin_bswap64((upx_uint64_t) v << 48);
return __builtin_bswap32(v << 16);
#endif
}
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);
#endif
}
static forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) noexcept {
return __builtin_bswap64(v);

View File

@ -43,6 +43,8 @@ int upx_doctest_check(int argc, char **argv) {
return 0;
#else
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)
return 0;
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 noinline void throwSomeValue(int x) {
static noinline void throwSomeValue(int x) may_throw {
if (x < 0)
throw int(x);
else
@ -346,6 +348,8 @@ static noinline void check_basic_cxx_exception_handling(void (*func)(int)) noexc
void upx_compiler_sanity_check(void) noexcept {
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 UPX_DEBUG_DOCTEST_DISABLE is set then we don't want to throw any
// 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);
}
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: */

View File

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

View File

@ -1238,8 +1238,9 @@ Linker* PackVmlinuxAMD64::newLinker() const
// instantiate instances
template class PackVmlinuxBase<ElfClass_LE32>;
template class PackVmlinuxBase<ElfClass_BE32>;
// template class PackVmlinuxBase<ElfClass_BE64>; // not used
template class PackVmlinuxBase<ElfClass_LE32>;
template class PackVmlinuxBase<ElfClass_LE64>;
/* 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;
try {
pb->initPackHeader();
@ -111,7 +111,7 @@ static tribool try_can_pack(PackerBase *pb, void *user) may_throw {
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;
try {
pb->initPackHeader();

View File

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

View File

@ -704,8 +704,9 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 {
if (rc != 0)
return rc;
// What could remain?
// make sort order deterministic
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); }

View File

@ -42,13 +42,13 @@
XSPAN_NAMESPACE_BEGIN
// HINT: set env-var "UPX_DEBUG_DOCTEST_DISABLE=1" for improved debugging experience
noinline void xspan_fail_nullptr(void);
noinline void xspan_fail_nullbase(void);
noinline void xspan_fail_not_same_base(void);
noinline void xspan_fail_range_nullptr(void);
noinline void xspan_fail_range_nullbase(void);
noinline void xspan_fail_range_range(void);
void xspan_check_range(const void *ptr, const void *base, ptrdiff_t size_in_bytes);
noinline void xspan_fail_nullptr(void) may_throw;
noinline void xspan_fail_nullbase(void) may_throw;
noinline void xspan_fail_not_same_base(void) may_throw;
noinline void xspan_fail_range_nullptr(void) may_throw;
noinline void xspan_fail_range_nullbase(void) may_throw;
noinline void xspan_fail_range_range(void) may_throw;
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
struct XSpanCount {