This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Segfault while unwinding an invalid function pointer


When the Backtrace function is called from a signal handler as a result of invalid function pointer call, the unwinding code will itself raise segv. This was first reported in http://gcc.gnu.org/ml/gcc/2007-06/msg00329.html

This fix is for x86_64 and adds a function that will determine if a memory range is "safe" before attempting to read it, so that the MD_FALLBACK_FRAME_STATE_FOR function will not segfault. If the memory range is invalid, it is determined to be a invalid function pointer call and the cfa is adjusted accordingly. The function which determines if a memory range is "safe" does not install a signal handler; instead it writes the memory range to a file and checks the result of bytes written to see if the memory was valid. Since the writes happen in kernel space, no segfault will be generated.

--
Pete Eberlein
IBM Linux Technology Center
Linux on Power Toolchain


2007-10-10 Pete Eberlein <eberlein@us.ibm.com>


	* linux-unwind.h (unwind_memory_range_check): New function
	that determines if a memory range is readable.
	* linux-unwind.h (x86_64_fallback_frame_state): Handle
	invalid function pointer address and change cfa.

Index: gcc/config/i386/linux-unwind.h
===================================================================
--- gcc/config/i386/linux-unwind.h      (revision 129215)
+++ gcc/config/i386/linux-unwind.h      (working copy)
@@ -39,6 +39,65 @@

#define MD_FALLBACK_FRAME_STATE_FOR x86_64_fallback_frame_state

+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define tmp_path "/tmp/null"
+
+static int
+unwind_memory_range_check (void *addr, size_t len)
+{
+  int rc;
+  int result = 0;
+  int tmp_null_f = -1;
+  int write_len = 0;
+  mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+
+  errno = 0;
+  if (tmp_null_f == -1)
+  {
+    tmp_null_f = open (tmp_path, (O_WRONLY | O_CREAT | O_TRUNC), mode);
+    if (tmp_null_f == -1) {
+      perror (" open (" tmp_path ") failed\n");
+      exit (1);
+    }
+  }
+  if (tmp_null_f != -1)
+  {
+    if ((write_len + len) > 4096)
+    {
+      if (write_len > 4096)
+      {
+        errno = 0;
+        rc = ftruncate (tmp_null_f, 0);
+        if (rc == -1)
+        {
+          perror ("ftruncate failed\n");
+        }
+      }
+      write_len = 0;
+      errno = 0;
+      rc = lseek (tmp_null_f, 0, SEEK_SET);
+
+      if (rc == -1)
+      {
+        perror ("lseek failed\n");
+      }
+    }
+    write_len += len;
+
+    rc = write (tmp_null_f, addr, len);
+    result = (rc == len);
+
+    close (tmp_null_f);
+    unlink (tmp_path);
+
+  }
+  return result;
+}
+
 static _Unwind_Reason_Code
 x86_64_fallback_frame_state (struct _Unwind_Context *context,
                             _Unwind_FrameState *fs)
@@ -47,6 +106,39 @@
   struct sigcontext *sc;
   long new_cfa;

+  if (!unwind_memory_range_check(pc, 5))
+    {
+      /* memory was invalid */
+
+      fs->regs.cfa_how = CFA_REG_OFFSET;
+      /* Register 7 is rsp  */
+      fs->regs.cfa_reg = 7;
+      fs->regs.cfa_offset = 8;
+
+      fs->regs.reg[0].how = REG_UNSAVED;
+      fs->regs.reg[1].how = REG_UNSAVED;
+      fs->regs.reg[2].how = REG_UNSAVED;
+      fs->regs.reg[3].how = REG_UNSAVED;
+      fs->regs.reg[4].how = REG_UNSAVED;
+      fs->regs.reg[5].how = REG_UNSAVED;
+      fs->regs.reg[6].how = REG_UNSAVED;
+      fs->regs.reg[8].how = REG_UNSAVED;
+      fs->regs.reg[9].how = REG_UNSAVED;
+      fs->regs.reg[10].how = REG_UNSAVED;
+      fs->regs.reg[11].how = REG_UNSAVED;
+      fs->regs.reg[12].how = REG_UNSAVED;
+      fs->regs.reg[13].how = REG_UNSAVED;
+      fs->regs.reg[14].how = REG_UNSAVED;
+      fs->regs.reg[15].how = REG_UNSAVED;
+      fs->regs.reg[16].how = REG_SAVED_OFFSET;
+      fs->regs.reg[16].loc.offset = -8;
+
+      fs->retaddr_column = 16;
+      fs->signal_frame = 0;
+
+      return _URC_NO_REASON;
+    }
+
   /* movq __NR_rt_sigreturn, %rax ; syscall  */
   if (*(unsigned char *)(pc+0) == 0x48
       && *(unsigned long *)(pc+1) == 0x050f0000000fc0c7)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]