This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Segfault while unwinding an invalid function pointer
- From: Pete Eberlein <eberlein at linux dot vnet dot ibm dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Wed, 10 Oct 2007 13:38:34 -0700
- Subject: [PATCH] Segfault while unwinding an invalid function pointer
- Reply-to: eberlein at us dot ibm dot com
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)