diff options
author | John David Anglin <danglin@gcc.gnu.org> | 2017-07-16 10:51:13 -0400 |
---|---|---|
committer | John David Anglin <danglin@gcc.gnu.org> | 2017-07-16 10:51:13 -0400 |
commit | df3d455b23f3b901ecc1e4b7f1a600943f04471b (patch) | |
tree | 1b5981b9acafc67d85550d715d115f2b0d7950a8 /sysdeps/hppa | |
parent | 66cd050f26c6e1130ff7998b7586c0003da23b46 (diff) | |
download | glibc-df3d455b23f3b901ecc1e4b7f1a600943f04471b.tar.gz glibc-df3d455b23f3b901ecc1e4b7f1a600943f04471b.tar.xz glibc-df3d455b23f3b901ecc1e4b7f1a600943f04471b.zip |
Fix [BZ 20098].
Diffstat (limited to 'sysdeps/hppa')
-rw-r--r-- | sysdeps/hppa/dl-fptr.c | 64 |
1 files changed, 46 insertions, 18 deletions
diff --git a/sysdeps/hppa/dl-fptr.c b/sysdeps/hppa/dl-fptr.c index 83bdb91202..96fd09d192 100644 --- a/sysdeps/hppa/dl-fptr.c +++ b/sysdeps/hppa/dl-fptr.c @@ -28,6 +28,7 @@ #include <dl-fptr.h> #include <dl-unmap-segments.h> #include <atomic.h> +#include <libc-pointer-arith.h> #ifndef ELF_MACHINE_BOOT_FPTR_TABLE_LEN /* ELF_MACHINE_BOOT_FPTR_TABLE_LEN should be greater than the number of @@ -181,24 +182,29 @@ make_fdesc (ElfW(Addr) ip, ElfW(Addr) gp) static inline ElfW(Addr) * __attribute__ ((always_inline)) make_fptr_table (struct link_map *map) { - const ElfW(Sym) *symtab - = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); ElfW(Addr) *fptr_table; size_t size; size_t len; + const ElfW(Sym) *symtabend; - /* XXX Apparently the only way to find out the size of the dynamic - symbol section is to assume that the string table follows right - afterwards... */ - len = ((strtab - (char *) symtab) + /* Determine the end of the dynamic symbol table using the hash. */ + if (map->l_info[DT_HASH] != NULL) + symtabend = (symtab + ((Elf_Symndx *) D_PTR (map, l_info[DT_HASH]))[1]); + else + /* There is no direct way to determine the number of symbols in the + dynamic symbol table and no hash table is present. The ELF + binary is ill-formed but what shall we do? Use the beginning of + the string table which generally follows the symbol table. */ + symtabend = (const ElfW(Sym) *) strtab; + + len = (((char *) symtabend - (char *) symtab) / map->l_info[DT_SYMENT]->d_un.d_val); - size = ((len * sizeof (fptr_table[0]) + GLRO(dl_pagesize) - 1) - & -GLRO(dl_pagesize)); - /* XXX We don't support here in the moment systems without MAP_ANON. - There probably are none for IA-64. In case this is proven wrong - we will have to open /dev/null here and use the file descriptor - instead of the hard-coded -1. */ + size = ALIGN_UP (len * sizeof (fptr_table[0]), GLRO(dl_pagesize)); + + /* We don't support systems without MAP_ANON. We avoid using malloc + because this might get called before malloc is setup. */ fptr_table = __mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); @@ -331,22 +337,45 @@ elf_machine_resolve (void) return addr; } +static inline int +_dl_read_access_allowed (unsigned int *addr) +{ + int result; + + asm ("proberi (%1),3,%0" : "=r" (result) : "r" (addr) : ); + + return result; +} + ElfW(Addr) _dl_lookup_address (const void *address) { ElfW(Addr) addr = (ElfW(Addr)) address; unsigned int *desc, *gptr; - /* Check for special cases. */ - if ((int) addr == -1 - || (unsigned int) addr < 4096 - || !((unsigned int) addr & 2)) + /* Return ADDR if the least-significant two bits of ADDR are not consistent + with ADDR being a linker defined function pointer. The normal value for + a code address in a backtrace is 3. */ + if (((unsigned int) addr & 3) != 2) + return addr; + + /* Handle special case where ADDR points to page 0. */ + if ((unsigned int) addr < 4096) return addr; /* Clear least-significant two bits from descriptor address. */ desc = (unsigned int *) ((unsigned int) addr & ~3); + if (!_dl_read_access_allowed (desc)) + return addr; - /* Check if descriptor requires resolution. The following trampoline is + /* Load first word of candidate descriptor. It should be a pointer + with word alignment and point to memory that can be read. */ + gptr = (unsigned int *) desc[0]; + if (((unsigned int) gptr & 3) != 0 + || !_dl_read_access_allowed (gptr)) + return addr; + + /* See if descriptor requires resolution. The following trampoline is used in each global offset table for function resolution: ldw 0(r20),r22 @@ -358,7 +387,6 @@ _dl_lookup_address (const void *address) .word "_dl_runtime_resolve ltp" got: .word _DYNAMIC .word "struct link map address" */ - gptr = (unsigned int *) desc[0]; if (gptr[0] == 0xea9f1fdd /* b,l .-12,r20 */ && gptr[1] == 0xd6801c1e /* depwi 0,31,2,r20 */ && (ElfW(Addr)) gptr[2] == elf_machine_resolve ()) |