From aa7c744796a067e1533b21e7d58aa30e6f3d12c8 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Sat, 7 Jul 2012 12:13:57 +0000 Subject: [PATCH] gdb/ * 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/ * gdbserver/linux-low.c (initialize_low): Call linux_ptrace_init_warnings. --- gdb/ChangeLog | 12 ++++ gdb/common/linux-ptrace.c | 124 ++++++++++++++++++++++++++++++++++++++ gdb/common/linux-ptrace.h | 1 + gdb/gdbserver/ChangeLog | 5 ++ gdb/gdbserver/linux-low.c | 1 + gdb/linux-nat.c | 2 + 6 files changed, 145 insertions(+) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 4ebefcc301..cab30f7733 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,15 @@ +2012-07-07 Jan Kratochvil + + * 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. + 2012-07-07 Jan Kratochvil * linux-thread-db.c (thread_db_find_new_threads_silently): Do not apply diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c index 600dcb9d77..fdec5d6291 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,126 @@ 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 +#include +#include +#include +#include + +#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; + + if (ptrace (PTRACE_KILL, child, NULL, NULL) != 0) + warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_KILL: %s"), + strerror (errno)); + else + { + int kill_status; + + got_pid = waitpid (child, &kill_status, 0); + gdb_assert (got_pid == child); + gdb_assert (WIFSIGNALED (kill_status)); + } + + /* + 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 6315b13627..96ad33d630 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/ChangeLog b/gdb/gdbserver/ChangeLog index 43e52405fe..3757add388 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,8 @@ +2012-07-07 Jan Kratochvil + + * gdbserver/linux-low.c (initialize_low): Call + linux_ptrace_init_warnings. + 2012-07-02 Doug Evans * mem-break.c (gdb_no_commands_at_breakpoint): Fix cast from diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 48134c3d98..a476031faf 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 8078a8055f..7ed666f0b1 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. */ -- 2.34.1