fix symtab-order-dependent spurious matches in dladdr
authorRich Felker <dalias@aerifal.cx>
Thu, 28 Jun 2018 15:51:43 +0000 (11:51 -0400)
committerRich Felker <dalias@aerifal.cx>
Thu, 28 Jun 2018 16:05:23 +0000 (12:05 -0400)
commit 8b8fb7f03721c42445f982582f462144ab60a1a0 added logic to prevent
matching a symbol with no recorded size (closest-match) when there is
an intervening symbol whose size was recorded, but it only worked when
the intervening symbol was encountered later in the search.

instead of rejecting symbols where addr falls outside their recorded
size during the closest-match search, accept them to find the true
closest-match, then reject such a result only once the search has
finished.

ldso/dynlink.c

index 31c50609b8bb0793c4c1a7a51f7007eed9c6c2d4..d963aeab1cc688bb253bcbeaa4f1446f3a5faa54 100644 (file)
@@ -1951,6 +1951,7 @@ int dladdr(const void *addr_arg, Dl_info *info)
        uint32_t nsym;
        char *strings;
        size_t best = 0;
+       size_t besterr = -1;
 
        pthread_rwlock_rdlock(&lock);
        p = addr2dso(addr);
@@ -1968,6 +1969,7 @@ int dladdr(const void *addr_arg, Dl_info *info)
                if (idx < nsym && (sym[idx].st_info&0xf) == STT_FUNC) {
                        best = (size_t)(p->funcdescs + idx);
                        bestsym = sym + idx;
+                       besterr = 0;
                }
        }
 
@@ -1978,18 +1980,19 @@ int dladdr(const void *addr_arg, Dl_info *info)
                        size_t symaddr = (size_t)laddr(p, sym->st_value);
                        if (symaddr > addr || symaddr < best)
                                continue;
-                       if (sym->st_size && symaddr+sym->st_size <= addr) {
-                               best = 0;
-                               bestsym = 0;
-                               continue;
-                       }
                        best = symaddr;
                        bestsym = sym;
+                       besterr = addr - symaddr;
                        if (addr == symaddr)
                                break;
                }
        }
 
+       if (bestsym && besterr > bestsym->st_size-1) {
+               best = 0;
+               bestsym = 0;
+       }
+
        info->dli_fname = p->name;
        info->dli_fbase = p->map;