From 8cce9cf64166444f88d05e7469386a86e9dabf0b Mon Sep 17 00:00:00 2001 From: John Reiser Date: Thu, 20 Feb 2025 09:00:25 -0800 Subject: [PATCH] Detect bad b_info.method https://issues.oss-fuzz.com/u/1/issues?q=upx modified: p_lx_elf.cpp modified: p_unix.cpp modified: packhead.cpp modified: packhead.h --- src/p_lx_elf.cpp | 24 ++++++++++++------------ src/p_unix.cpp | 4 ++-- src/packhead.cpp | 7 +++++++ src/packhead.h | 1 + 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index a3bbf66e..e8f855ab 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -4300,7 +4300,7 @@ void PackLinuxElf32::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, filesz); ft = orig_ft; ph = orig_ph; - ph.method = ph_force_method(methods[k]); + ph.set_method(ph_force_method(methods[k]), offset); ph.u_len = filesz; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -4313,7 +4313,7 @@ void PackLinuxElf32::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, sz_tail); ft = orig_ft; ph = orig_ph; - ph.method = ph_force_method(methods[k]); + ph.set_method(ph_force_method(methods[k])); ph.u_len = sz_tail; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -4326,7 +4326,7 @@ void PackLinuxElf32::pack1(OutputFile * /*fo*/, Filter &ft) } ft = orig_ft; ph = orig_ph; - ph.method = ph_force_method(method_best); + ph.set_method(ph_force_method(method_best)); } Elf32_Phdr *phdr = phdri; @@ -5152,7 +5152,7 @@ void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, filesz); ft = orig_ft; ph = orig_ph; - ph.method = ph_force_method(methods[k]); + ph.set_method(ph_force_method(methods[k])); ph.u_len = filesz; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -5165,7 +5165,7 @@ void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, sz_tail); ft = orig_ft; ph = orig_ph; - ph.method = ph_force_method(methods[k]); + ph.set_method(ph_force_method(methods[k])); ph.u_len = sz_tail; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -5178,7 +5178,7 @@ void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft) } ft = orig_ft; ph = orig_ph; - ph.method = ph_force_method(method_best); + ph.set_method(ph_force_method(method_best)); } Elf64_Phdr *phdr = phdri; @@ -7620,7 +7620,7 @@ void PackLinuxElf64::unpack(OutputFile *fo) fi->readx(&bhdr, szb_info); ph.u_len = get_te32(&bhdr.sz_unc); ph.c_len = get_te32(&bhdr.sz_cpr); - ph.method = bhdr.b_method; + ph.set_method(bhdr.b_method, overlay_offset + sizeof(p_info)); if (ph.c_len > file_size_u || ph.c_len == 0 || ph.u_len == 0 || ph.u_len > orig_file_size) throwCantUnpack("b_info corrupted"); @@ -7665,11 +7665,11 @@ void PackLinuxElf64::unpack(OutputFile *fo) unsigned b_method = ibuf[0]; unsigned b_extra = ibuf[3]; if (M_ZSTD >= b_method && 0 == b_extra) { - fi->seek( -(upx_off_t)(ph.c_len + szb_info), SEEK_CUR); + unsigned where = fi->seek( -(upx_off_t)(ph.c_len + szb_info), SEEK_CUR); szb_info = 12; fi->readx(&bhdr, szb_info); ph.filter_cto = bhdr.b_cto8; - ph.method = bhdr.b_method; + ph.set_method(bhdr.b_method, where); prev_method = bhdr.b_method; // FIXME if multiple de-compressors fi->readx(ibuf, ph.c_len); } @@ -7867,7 +7867,7 @@ void PackLinuxElf64::unpack(OutputFile *fo) // check for end-of-file fi->readx(&bhdr, szb_info); - ph.method = bhdr.b_method; + ph.set_method(bhdr.b_method, ~0u); unsigned const sz_unc = ph.u_len = get_te32(&bhdr.sz_unc); if (sz_unc == 0) { // uncompressed size 0 -> EOF @@ -8851,7 +8851,7 @@ void PackLinuxElf32::unpack(OutputFile *fo) fi->readx(&bhdr, szb_info); ph.u_len = get_te32(&bhdr.sz_unc); ph.c_len = get_te32(&bhdr.sz_cpr); - ph.method = bhdr.b_method; + ph.set_method(bhdr.b_method, overlay_offset + sizeof(p_info)); if (ph.c_len > (unsigned)file_size || ph.c_len == 0 || ph.u_len == 0 || ph.u_len > orig_file_size) throwCantUnpack("b_info corrupted"); @@ -9076,7 +9076,7 @@ void PackLinuxElf32::unpack(OutputFile *fo) // check for end-of-file fi->readx(&bhdr, szb_info); - ph.method = bhdr.b_method; + ph.set_method(bhdr.b_method, ~0u); unsigned const sz_unc = ph.u_len = get_te32(&bhdr.sz_unc); if (sz_unc == 0) { // uncompressed size 0 -> EOF diff --git a/src/p_unix.cpp b/src/p_unix.cpp index 1c7db688..16f36988 100644 --- a/src/p_unix.cpp +++ b/src/p_unix.cpp @@ -485,7 +485,6 @@ unsigned PackUnix::unpackExtent(unsigned wanted, OutputFile *fo, fi->readx(&hdr, szb_info); int const sz_unc = ph.u_len = get_te32(&hdr.sz_unc); int const sz_cpr = ph.c_len = get_te32(&hdr.sz_cpr); - ph.method = hdr.b_method; ph.filter_cto = hdr.b_cto8; if (sz_unc == 0 || M_LZMA < hdr.b_method) { @@ -508,6 +507,7 @@ unsigned PackUnix::unpackExtent(unsigned wanted, OutputFile *fo, c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler); if (sz_cpr < sz_unc) { // block was compressed + ph.set_method(hdr.b_method); decompress(ibuf+j, ibuf+inlen, false); if (12==szb_info) { // modern per-block filter if (hdr.b_ftid) { @@ -658,7 +658,7 @@ void PackUnix::unpack(OutputFile *fo) fi->readx(&bhdr, szb_info); ph.u_len = sz_unc = get_te32(&bhdr.sz_unc); ph.c_len = sz_cpr = get_te32(&bhdr.sz_cpr); - ph.method = bhdr.b_method; + ph.set_method(bhdr.b_method); if (sz_unc == 0) // uncompressed size 0 -> EOF { diff --git a/src/packhead.cpp b/src/packhead.cpp index 4f97924b..4f293f39 100644 --- a/src/packhead.cpp +++ b/src/packhead.cpp @@ -45,6 +45,13 @@ void PackHeader::reset() noexcept { compress_result.reset(); } +int PackHeader::set_method(int m, unsigned offset) { + unsigned mc = ~(0x80u << 24) & m; // see ph_forced_method + if ((mc < M_NRV2B_LE32 || M_LZMA < mc) && ~0u != offset) + throwCantPack("bad method %#x at %#x", (unsigned) m, offset); + return method = m; +} + /************************************************************************* // extremely simple checksum for the header itself (since version 10) **************************************************************************/ diff --git a/src/packhead.h b/src/packhead.h index 97a2fd4b..e2fb266f 100644 --- a/src/packhead.h +++ b/src/packhead.h @@ -63,6 +63,7 @@ struct PackHeader final { int n_mru; // specific name for filter ctojr }; int header_checksum; + int set_method(int m, unsigned offset = 0); // check, then assign // support fields for verifying decompression unsigned saved_u_adler;