diff mbox

[PATCH:,hppa] Fix segmentation faults in _dl_lookup_address

Message ID EDB21E9C-CC9E-49D5-85F7-A2BC25E1F989@bell.net
State New
Headers show

Commit Message

John David Anglin May 14, 2016, 11:14 p.m. UTC
The attached change fixes a regression in the debug/backtrace-tst test.  It tightens up the checks
for a valid function pointers.  It also uses the proberi instruction to avoid faulting when the address is
not a valid function pointer.  However, it's probably not possible to completely ensure that the address
lookup is correct given an arbitrary address.

Please install if okay.

Dave
--
John David Anglin	dave.anglin@bell.net
2016-05-14  John David Anglin  <danglin@gcc.gnu.org>

	[BZ 20098]
	* sysdeps/hppa/dl-fptr.c (_dl_read_access_allowed): New.
	(_dl_lookup_address): Return address if it is not consistent with
	being a linker defined function pointer.  Likewise, return address
	if address and function descriptor addresses are not accessible.
diff mbox

Patch

diff --git a/sysdeps/hppa/dl-fptr.c b/sysdeps/hppa/dl-fptr.c
index 083242b..af0c5a1 100644
--- a/sysdeps/hppa/dl-fptr.c
+++ b/sysdeps/hppa/dl-fptr.c
@@ -331,22 +331,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;
+
+  /* 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;
 
-  /* Check if descriptor requires resolution.  The following trampoline is
+  /* See if descriptor requires resolution.  The following trampoline is
      used in each global offset table for function resolution:
 
 		ldw 0(r20),r22
@@ -358,7 +381,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 ())