CVE-2020-27790: Divide By Zero (CWE-369)
value: A floating point exception issue was discovered in UPX in PackLinuxElf64::invert_pt_dynamic() function of p_lx_elf.cpp file. An attacker with a crafted input file could trigger this issue that could cause a crash leading to a denial of service. The highest impact is to Availability.
CWE description: The product divides a value by zero.
CVSS v3 base score: 5.5
Published: 2022-08-18T19:15Z
Repository: https://github.com/upx/upx
Language: C++
Files changed: src/p_lx_elf.cpp
Commit date: 2020-01-13 17:10:02 -0800
Commit message: Detect bogus DT_SYMENT.

https://github.com/upx/upx/issues/331
	modified:   p_lx_elf.cpp

Diff: diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp
index c17c672d..ef58eee8 100644
--- a/src/p_lx_elf.cpp
+++ b/src/p_lx_elf.cpp
@@ -1612,13 +1612,21 @@ PackLinuxElf32::invert_pt_dynamic(Elf32_Dyn const *dynp)
         upx_uint32_t const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
         upx_uint32_t const v_str = get_te32(&dynp0[-1+ x_str].d_val);
         unsigned const  z_sym = dt_table[Elf32_Dyn::DT_SYMENT];
         unsigned const sz_sym = !z_sym ? sizeof(Elf32_Sym)
             : get_te32(&dynp0[-1+ z_sym].d_val);
+        if (sz_sym < sizeof(Elf32_Sym)) {
+            char msg[50]; snprintf(msg, sizeof(msg),
+                "bad DT_SYMENT %x", sz_sym);
+            throwCantPack(msg);
+        }
         if (v_sym < v_str) {
             symnum_end = (v_str - v_sym) / sz_sym;
         }
+        if (symnum_end < 1) {
+            throwCantPack("bad DT_SYMTAB");
+        }
     }
     // DT_HASH often ends at DT_SYMTAB
     unsigned const v_hsh = elf_unsigned_dynamic(Elf32_Dyn::DT_HASH);
     if (v_hsh && file_image) {
         hashtab = (unsigned const *)elf_find_dynamic(Elf32_Dyn::DT_HASH);
@@ -5102,13 +5110,21 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp)
         upx_uint64_t const v_sym = get_te64(&dynp0[-1+ x_sym].d_val);
         upx_uint64_t const v_str = get_te64(&dynp0[-1+ x_str].d_val);
         unsigned const  z_sym = dt_table[Elf64_Dyn::DT_SYMENT];
         unsigned const sz_sym = !z_sym ? sizeof(Elf64_Sym)
             : get_te64(&dynp0[-1+ z_sym].d_val);
+        if (sz_sym < sizeof(Elf64_Sym)) {
+            char msg[50]; snprintf(msg, sizeof(msg),
+                "bad DT_SYMENT %x", sz_sym);
+            throwCantPack(msg);
+        }
         if (v_sym < v_str) {
             symnum_end = (v_str - v_sym) / sz_sym;
         }
+        if (symnum_end < 1) {
+            throwCantPack("bad DT_SYMTAB");
+        }
     }
     // DT_HASH often ends at DT_SYMTAB
     unsigned const v_hsh = elf_unsigned_dynamic(Elf64_Dyn::DT_HASH);
     if (v_hsh && file_image) {
         hashtab = (unsigned const *)elf_find_dynamic(Elf64_Dyn::DT_HASH);

Fixed code:         if (sz_sym < sizeof(Elf32_Sym)) {
            char msg[50]; snprintf(msg, sizeof(msg),
                "bad DT_SYMENT %x", sz_sym);
            throwCantPack(msg);
        }
        if (symnum_end < 1) {
            throwCantPack("bad DT_SYMTAB");
        }
        if (sz_sym < sizeof(Elf64_Sym)) {
            char msg[50]; snprintf(msg, sizeof(msg),
                "bad DT_SYMENT %x", sz_sym);
            throwCantPack(msg);
        }
        if (symnum_end < 1) {
            throwCantPack("bad DT_SYMTAB");
        }