From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18563 invoked by alias); 3 Jul 2012 15:02:28 -0000 Received: (qmail 18547 invoked by uid 22791); 3 Jul 2012 15:02:24 -0000 X-SWARE-Spam-Status: No, hits=-6.3 required=5.0 tests=AWL,BAYES_00,KHOP_RCVD_UNTRUST,RCVD_IN_DNSWL_HI,RCVD_IN_HOSTKARMA_W,SPF_HELO_PASS,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 03 Jul 2012 15:02:00 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q63F20Gf008779 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 3 Jul 2012 11:02:00 -0400 Received: from host2.jankratochvil.net (ovpn-116-32.ams2.redhat.com [10.36.116.32]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q63F1tTN029600 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO) for ; Tue, 3 Jul 2012 11:01:58 -0400 Date: Tue, 03 Jul 2012 15:02:00 -0000 From: Jan Kratochvil To: gdb-patches@sourceware.org Subject: [patchv2] ON_STACK: Warn on buggy Linux i386 kernels Message-ID: <20120703150154.GA26478@host2.jankratochvil.net> References: <20120703142709.GA21059@host2.jankratochvil.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20120703142709.GA21059@host2.jankratochvil.net> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-07/txt/msg00038.txt.bz2 On Tue, 03 Jul 2012 16:27:09 +0200, Jan Kratochvil wrote: [ Just added forgotten killing of the test child. ] gdb/ 2012-07-03 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. gdb/gdbserver/ 2012-07-03 Jan Kratochvil * 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..fdec5d6 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 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. */