CVE-2020-27787: Insufficient Information (NVD-CWE-noinfo)
value: A Segmentaation fault was found in UPX in invert_pt_dynamic() function in p_lx_elf.cpp. An attacker with a crafted input file allows invalid memory address access that could lead to a denial of service.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
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-14 18:45:52 -0800
Commit message: Detect 0==DT_SYMTAB in invert_pt_dynamic()

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

Diff: diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp
index 96f5838d..70349487 100644
--- a/src/p_lx_elf.cpp
+++ b/src/p_lx_elf.cpp
@@ -1637,12 +1637,12 @@ PackLinuxElf32::invert_pt_dynamic(Elf32_Dyn const *dynp)
         }
         unsigned const nbucket = get_te32(&hashtab[0]);
         unsigned const *const buckets = &hashtab[2];
         unsigned const *const chains = &buckets[nbucket]; (void)chains;
 
-        unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
-        if (!nbucket
+        unsigned const v_sym = !x_sym ? 0 : get_te32(&dynp0[-1+ x_sym].d_val);
+        if (!nbucket || !v_sym
         || (nbucket>>31) || (file_size/sizeof(unsigned)) <= (2*nbucket)  // FIXME: weak
         || ((v_hsh < v_sym) && (v_sym - v_hsh) < (sizeof(unsigned)*2  // headers
                 + sizeof(*buckets)*nbucket  // buckets
                 + sizeof(*chains) *nbucket  // chains
            ))
@@ -1668,12 +1668,12 @@ PackLinuxElf32::invert_pt_dynamic(Elf32_Dyn const *dynp)
         unsigned const *const bitmask = (unsigned const *)(void const *)&gashtab[4];
         unsigned     const *const buckets = (unsigned const *)&bitmask[n_bitmask];
         unsigned     const *const hasharr = &buckets[n_bucket]; (void)hasharr;
       //unsigned     const *const gashend = &hasharr[n_bucket];  // minimum
 
-        unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
-        if (!n_bucket || !n_bitmask
+        unsigned const v_sym = !x_sym ? 0 : get_te32(&dynp0[-1+ x_sym].d_val);
+        if (!n_bucket || !n_bitmask || !v_sym
         || (-1+ n_bitmask) & n_bitmask  // not a power of 2
         || 8*sizeof(unsigned) <= gnu_shift  // shifted result always == 0
         || (n_bucket>>30)  // fie on fuzzers
         || (n_bitmask>>30)
         || (file_size / sizeof(unsigned)) <= (n_bitmask + 2*n_bucket)  // FIXME: weak
@@ -5137,12 +5137,12 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp)
         }
         unsigned const nbucket = get_te32(&hashtab[0]);
         unsigned const *const buckets = &hashtab[2];
         unsigned const *const chains = &buckets[nbucket]; (void)chains;
 
-        unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
-        if (!nbucket
+        unsigned const v_sym = !x_sym ? 0 : get_te32(&dynp0[-1+ x_sym].d_val);
+        if (!nbucket || !v_sym
         || (nbucket>>31) || (file_size/sizeof(unsigned)) <= (2*nbucket)  // FIXME: weak
         || ((v_hsh < v_sym) && (v_sym - v_hsh) < (sizeof(unsigned)*2  // headers
                 + sizeof(*buckets)*nbucket  // buckets
                 + sizeof(*chains) *nbucket  // chains
            ))
@@ -5168,12 +5168,12 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp)
         upx_uint64_t const *const bitmask = (upx_uint64_t const *)(void const *)&gashtab[4];
         unsigned     const *const buckets = (unsigned const *)&bitmask[n_bitmask];
         unsigned     const *const hasharr = &buckets[n_bucket]; (void)hasharr;
       //unsigned     const *const gashend = &hasharr[n_bucket];  // minimum
 
-        upx_uint64_t const v_sym = get_te64(&dynp0[-1+ x_sym].d_val);
-        if (!n_bucket || !n_bitmask
+        upx_uint64_t const v_sym = !x_sym ? 0 : get_te64(&dynp0[-1+ x_sym].d_val);
+        if (!n_bucket || !n_bitmask || !v_sym
         || (-1+ n_bitmask) & n_bitmask  // not a power of 2
         || 8*sizeof(upx_uint64_t) <= gnu_shift  // shifted result always == 0
         || (n_bucket>>30)  // fie on fuzzers
         || (n_bitmask>>30)
         || (file_size/sizeof(unsigned)) <= ((sizeof(*bitmask)/sizeof(unsigned))*n_bitmask + 2*n_bucket)  // FIXME: weak

Vulnerable code:         unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
        if (!nbucket
        unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
        if (!n_bucket || !n_bitmask
        unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
        if (!nbucket
        upx_uint64_t const v_sym = get_te64(&dynp0[-1+ x_sym].d_val);
        if (!n_bucket || !n_bitmask
Fixed code:         unsigned const v_sym = !x_sym ? 0 : get_te32(&dynp0[-1+ x_sym].d_val);
        if (!nbucket || !v_sym
        unsigned const v_sym = !x_sym ? 0 : get_te32(&dynp0[-1+ x_sym].d_val);
        if (!n_bucket || !n_bitmask || !v_sym
        unsigned const v_sym = !x_sym ? 0 : get_te32(&dynp0[-1+ x_sym].d_val);
        if (!nbucket || !v_sym
        upx_uint64_t const v_sym = !x_sym ? 0 : get_te64(&dynp0[-1+ x_sym].d_val);
        if (!n_bucket || !n_bitmask || !v_sym