This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB 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] ON_STACK: Warn on buggy Linux i386 kernels


Hi,

as discussed in:
	Possible future NX ON_STACk regression Re: [patch 3/3] Use ON_STACK for i386/amd64 (gdb2495.exp regression)
	http://sourceware.org/ml/gdb-patches/2012-06/msg00568.html

I have found the problem with ON_STACK inferior calls affects only i386 Linux
kernels (not x86_64, nor Fedora i386 PAE).  It was fixed/removed in Fedora by 
	commit 88fa1f0332d188795ed73d7ac2b1564e11a0b4cd
	Author: Dave Jones <davej@redhat.com>
	Date:   Mon Jun 4 11:56:08 2012 -0400
	    disable 32bit nx, it seems to be broken
	http://pkgs.fedoraproject.org/gitweb/?p=kernel.git;a=commitdiff;h=88fa1f0332d188795ed73d7ac2b1564e11a0b4cd
	http://pkgs.fedoraproject.org/gitweb/?p=kernel.git;a=blob_plain;f=linux-2.6-32bit-mmap-exec-randomization.patch;hb=88fa1f0332d188795ed73d7ac2b1564e11a0b4cd
	http://pkgs.fedoraproject.org/gitweb/?p=kernel.git;a=blob_plain;f=linux-2.6-i386-nx-emulation.patch;hb=88fa1f0332d188795ed73d7ac2b1564e11a0b4cd
	http://pkgs.fedoraproject.org/gitweb/?p=kernel.git;a=blob_plain;f=nx-emu-remove-cpuinitdata-for-disable_nx-on-x86_32.patch;hb=88fa1f0332d188795ed73d7ac2b1564e11a0b4cd
	http://pkgs.fedoraproject.org/gitweb/?p=kernel.git;a=blob_plain;f=shlib_base_randomize.patch;hb=88fa1f0332d188795ed73d7ac2b1564e11a0b4cd
So the last Fedora kernel where it was reproducible is: kernel-3.4.0-1.fc17.i686

It does not seem to be ever present in upstream ("vanilla") Linux kernels.

But I have found ubuntu-12.04-dvd-i386.iso (both GA and latest updates) which
is LTS (=still maintained) is also affected by this problem so I expect there
maybe vocal feedback.

It puts there only a warning.  As it is both off-trunk patch and also the
patch is present in neither old or new (at least Fedora) kernels and as it
does not affect the most basic debugging I did not find it worth a workaround.
Rather Ubuntu should provide a fix IMO.


Thanks,
Jan


gdb/
2012-07-03  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* common/linux-ptrace.c: Include gdb_assert.h.
	<__i386__> (linux_ptrace_test_ret_to_nx_instr): New declaration.
	<__i386__>: Include sys/reg.h, sys/mman.h, signal.h, sys/wait.h and
	stdint.h.
	(linux_ptrace_test_ret_to_nx, linux_ptrace_init_warnings): New
	functions.
	* common/linux-ptrace.h (linux_ptrace_init_warnings): New declarations.
	* linux-nat.c (linux_child_post_attach)
	(linux_child_post_startup_inferior): Call linux_ptrace_init_warnings.

gdb/gdbserver/
2012-07-03  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdbserver/linux-low.c (initialize_low): Call
	linux_ptrace_init_warnings.

diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
index 600dcb9..10f77c9 100644
--- a/gdb/common/linux-ptrace.c
+++ b/gdb/common/linux-ptrace.c
@@ -26,6 +26,7 @@
 #include "linux-ptrace.h"
 #include "linux-procfs.h"
 #include "buffer.h"
+#include "gdb_assert.h"
 
 /* Find all possible reasons we could fail to attach PID and append these
    newline terminated reason strings to initialized BUFFER.  '\0' termination
@@ -47,3 +48,114 @@ linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer)
 				 "- the process has already terminated\n"),
 		       (int) pid);
 }
+
+#ifdef __i386__
+
+/* Address of the 'ret' instruction in asm code block below.  */
+extern void (linux_ptrace_test_ret_to_nx_instr) (void);
+
+#include <sys/reg.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <stdint.h>
+
+#endif /* __i386__ */
+
+/* Test broken off-trunk Linux kernel patchset for NX support on i386.  It was
+   removed in Fedora kernel 88fa1f0332d188795ed73d7ac2b1564e11a0b4cd.  */
+
+static void
+linux_ptrace_test_ret_to_nx (void)
+{
+#ifdef __i386__
+  pid_t child, got_pid;
+  gdb_byte *return_address, *pc;
+  long l;
+  int status;
+
+  return_address = mmap (NULL, 2, PROT_READ | PROT_WRITE,
+			 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (return_address == MAP_FAILED)
+    {
+      warning (_("linux_ptrace_test_ret_to_nx: Cannot mmap: %s"),
+	       strerror (errno));
+      return;
+    }
+
+  /* Put there 'int3'.  */
+  *return_address = 0xcc;
+
+  child = fork ();
+  switch (child)
+    {
+    case -1:
+      warning (_("linux_ptrace_test_ret_to_nx: Cannot fork: %s"),
+	       strerror (errno));
+      return;
+
+    case 0:
+      l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+      if (l != 0)
+	warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: %s"),
+		 strerror (errno));
+      else
+	{
+	  asm volatile ("pushl %0;"
+			".globl linux_ptrace_test_ret_to_nx_instr;"
+			"linux_ptrace_test_ret_to_nx_instr:"
+			"ret"
+			: : "r" (return_address) : "%esp", "memory");
+	  gdb_assert_not_reached ("asm block did not terminate");
+	}
+
+      _exit (1);
+    }
+
+  got_pid = waitpid (child, &status, 0);
+  gdb_assert (got_pid == child);
+  gdb_assert (WIFSTOPPED (status));
+
+  /* We may get SIGSEGV due to missing PROT_EXEC of the return_address.  */
+  gdb_assert (WSTOPSIG (status) == SIGTRAP || WSTOPSIG (status) == SIGSEGV);
+
+  errno = 0;
+  l = ptrace (PTRACE_PEEKUSER, child, (void *) (uintptr_t) (EIP * 4), NULL);
+  gdb_assert (errno == 0);
+  pc = (void *) (uintptr_t) l;
+
+  /* + 1 is there as x86* stops after the 'int3' instruction.  */
+  if (WSTOPSIG (status) == SIGTRAP && pc == return_address + 1)
+    {
+      /* PASS */
+      return;
+    }
+
+  /* We may get SIGSEGV due to missing PROT_EXEC of the RETURN_ADDRESS page.  */
+  if (WSTOPSIG (status) == SIGSEGV && pc == return_address)
+    {
+      /* PASS */
+      return;
+    }
+
+  gdb_assert ((void (*) (void)) pc == &linux_ptrace_test_ret_to_nx_instr);
+
+  warning (_("Cannot call inferior functions, you have broken "
+	     "Linux kernel i386 NX (non-executable pages) support!"));
+#endif /* __i386__ */
+}
+
+/* Display possible problems on this system.  Display them only once per GDB
+   execution.  */
+
+void
+linux_ptrace_init_warnings (void)
+{
+  static int warned = 0;
+
+  if (warned)
+    return;
+  warned = 1;
+
+  linux_ptrace_test_ret_to_nx ();
+}
diff --git a/gdb/common/linux-ptrace.h b/gdb/common/linux-ptrace.h
index 6315b13..96ad33d 100644
--- a/gdb/common/linux-ptrace.h
+++ b/gdb/common/linux-ptrace.h
@@ -68,5 +68,6 @@ struct buffer;
 #endif
 
 extern void linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer);
+extern void linux_ptrace_init_warnings (void);
 
 #endif /* COMMON_LINUX_PTRACE_H */
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 48134c3..a476031 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5878,6 +5878,7 @@ initialize_low (void)
 		       the_low_target.breakpoint_len);
   linux_init_signals ();
   linux_test_for_tracefork ();
+  linux_ptrace_init_warnings ();
 #ifdef HAVE_LINUX_REGSETS
   for (num_regsets = 0; target_regsets[num_regsets].size >= 0; num_regsets++)
     ;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index b82c248..d3a870f 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -583,6 +583,7 @@ linux_child_post_attach (int pid)
 {
   linux_enable_event_reporting (pid_to_ptid (pid));
   linux_enable_tracesysgood (pid_to_ptid (pid));
+  linux_ptrace_init_warnings ();
 }
 
 static void
@@ -590,6 +591,7 @@ linux_child_post_startup_inferior (ptid_t ptid)
 {
   linux_enable_event_reporting (ptid);
   linux_enable_tracesysgood (ptid);
+  linux_ptrace_init_warnings ();
 }
 
 /* Return the number of known LWPs in the tgid given by PID.  */


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