OLD | NEW |
(Empty) | |
| 1 /* Linux-specific ptrace manipulation routines. |
| 2 Copyright (C) 2012 Free Software Foundation, Inc. |
| 3 |
| 4 This file is part of GDB. |
| 5 |
| 6 This program is free software; you can redistribute it and/or modify |
| 7 it under the terms of the GNU General Public License as published by |
| 8 the Free Software Foundation; either version 3 of the License, or |
| 9 (at your option) any later version. |
| 10 |
| 11 This program is distributed in the hope that it will be useful, |
| 12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 GNU General Public License for more details. |
| 15 |
| 16 You should have received a copy of the GNU General Public License |
| 17 along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| 18 |
| 19 #ifdef GDBSERVER |
| 20 #include "server.h" |
| 21 #else |
| 22 #include "defs.h" |
| 23 #include "gdb_string.h" |
| 24 #endif |
| 25 |
| 26 #include "linux-ptrace.h" |
| 27 #include "linux-procfs.h" |
| 28 #include "buffer.h" |
| 29 #include "gdb_assert.h" |
| 30 |
| 31 /* Find all possible reasons we could fail to attach PID and append these |
| 32 newline terminated reason strings to initialized BUFFER. '\0' termination |
| 33 of BUFFER must be done by the caller. */ |
| 34 |
| 35 void |
| 36 linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer) |
| 37 { |
| 38 pid_t tracerpid; |
| 39 |
| 40 tracerpid = linux_proc_get_tracerpid (pid); |
| 41 if (tracerpid > 0) |
| 42 buffer_xml_printf (buffer, _("warning: process %d is already traced " |
| 43 "by process %d\n"), |
| 44 (int) pid, (int) tracerpid); |
| 45 |
| 46 if (linux_proc_pid_is_zombie (pid)) |
| 47 buffer_xml_printf (buffer, _("warning: process %d is a zombie " |
| 48 "- the process has already terminated\n"), |
| 49 (int) pid); |
| 50 } |
| 51 |
| 52 #if defined __i386__ || defined __x86_64__ |
| 53 |
| 54 /* Address of the 'ret' instruction in asm code block below. */ |
| 55 extern void (linux_ptrace_test_ret_to_nx_instr) (void); |
| 56 |
| 57 #include <sys/reg.h> |
| 58 #include <sys/mman.h> |
| 59 #include <signal.h> |
| 60 #include <sys/wait.h> |
| 61 #include <stdint.h> |
| 62 |
| 63 #endif /* defined __i386__ || defined __x86_64__ */ |
| 64 |
| 65 /* Test broken off-trunk Linux kernel patchset for NX support on i386. It was |
| 66 removed in Fedora kernel 88fa1f0332d188795ed73d7ac2b1564e11a0b4cd. |
| 67 |
| 68 Test also x86_64 arch for PaX support. */ |
| 69 |
| 70 static void |
| 71 linux_ptrace_test_ret_to_nx (void) |
| 72 { |
| 73 #if defined __i386__ || defined __x86_64__ |
| 74 pid_t child, got_pid; |
| 75 gdb_byte *return_address, *pc; |
| 76 long l; |
| 77 int status; |
| 78 |
| 79 return_address = mmap (NULL, 2, PROT_READ | PROT_WRITE, |
| 80 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
| 81 if (return_address == MAP_FAILED) |
| 82 { |
| 83 warning (_("linux_ptrace_test_ret_to_nx: Cannot mmap: %s"), |
| 84 strerror (errno)); |
| 85 return; |
| 86 } |
| 87 |
| 88 /* Put there 'int3'. */ |
| 89 *return_address = 0xcc; |
| 90 |
| 91 child = fork (); |
| 92 switch (child) |
| 93 { |
| 94 case -1: |
| 95 warning (_("linux_ptrace_test_ret_to_nx: Cannot fork: %s"), |
| 96 strerror (errno)); |
| 97 return; |
| 98 |
| 99 case 0: |
| 100 l = ptrace (PTRACE_TRACEME, 0, NULL, NULL); |
| 101 if (l != 0) |
| 102 warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: %s"), |
| 103 strerror (errno)); |
| 104 else |
| 105 { |
| 106 #if defined __i386__ |
| 107 asm volatile ("pushl %0;" |
| 108 ".globl linux_ptrace_test_ret_to_nx_instr;" |
| 109 "linux_ptrace_test_ret_to_nx_instr:" |
| 110 "ret" |
| 111 : : "r" (return_address) : "%esp", "memory"); |
| 112 #elif defined __x86_64__ |
| 113 asm volatile ("pushq %0;" |
| 114 ".globl linux_ptrace_test_ret_to_nx_instr;" |
| 115 "linux_ptrace_test_ret_to_nx_instr:" |
| 116 "ret" |
| 117 : : "r" (return_address) : "%rsp", "memory"); |
| 118 #else |
| 119 # error "!__i386__ && !__x86_64__" |
| 120 #endif |
| 121 gdb_assert_not_reached ("asm block did not terminate"); |
| 122 } |
| 123 |
| 124 _exit (1); |
| 125 } |
| 126 |
| 127 errno = 0; |
| 128 got_pid = waitpid (child, &status, 0); |
| 129 if (got_pid != child) |
| 130 { |
| 131 warning (_("linux_ptrace_test_ret_to_nx: waitpid returned %ld: %s"), |
| 132 (long) got_pid, strerror (errno)); |
| 133 return; |
| 134 } |
| 135 |
| 136 if (WIFSIGNALED (status)) |
| 137 { |
| 138 if (WTERMSIG (status) != SIGKILL) |
| 139 warning (_("linux_ptrace_test_ret_to_nx: WTERMSIG %d is not SIGKILL!"), |
| 140 (int) WTERMSIG (status)); |
| 141 else |
| 142 warning (_("Cannot call inferior functions, Linux kernel PaX " |
| 143 "protection forbids return to non-executable pages!")); |
| 144 return; |
| 145 } |
| 146 |
| 147 if (!WIFSTOPPED (status)) |
| 148 { |
| 149 warning (_("linux_ptrace_test_ret_to_nx: status %d is not WIFSTOPPED!"), |
| 150 status); |
| 151 return; |
| 152 } |
| 153 |
| 154 /* We may get SIGSEGV due to missing PROT_EXEC of the return_address. */ |
| 155 if (WSTOPSIG (status) != SIGTRAP && WSTOPSIG (status) != SIGSEGV) |
| 156 { |
| 157 warning (_("linux_ptrace_test_ret_to_nx: " |
| 158 "WSTOPSIG %d is neither SIGTRAP nor SIGSEGV!"), |
| 159 (int) WSTOPSIG (status)); |
| 160 return; |
| 161 } |
| 162 |
| 163 errno = 0; |
| 164 #if defined __i386__ |
| 165 l = ptrace (PTRACE_PEEKUSER, child, (void *) (uintptr_t) (EIP * 4), NULL); |
| 166 #elif defined __x86_64__ |
| 167 l = ptrace (PTRACE_PEEKUSER, child, (void *) (uintptr_t) (RIP * 8), NULL); |
| 168 #else |
| 169 # error "!__i386__ && !__x86_64__" |
| 170 #endif |
| 171 if (errno != 0) |
| 172 { |
| 173 warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_PEEKUSER: %s"), |
| 174 strerror (errno)); |
| 175 return; |
| 176 } |
| 177 pc = (void *) (uintptr_t) l; |
| 178 |
| 179 if (ptrace (PTRACE_KILL, child, NULL, NULL) != 0) |
| 180 { |
| 181 warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_KILL: %s"), |
| 182 strerror (errno)); |
| 183 return; |
| 184 } |
| 185 else |
| 186 { |
| 187 int kill_status; |
| 188 |
| 189 errno = 0; |
| 190 got_pid = waitpid (child, &kill_status, 0); |
| 191 if (got_pid != child) |
| 192 { |
| 193 warning (_("linux_ptrace_test_ret_to_nx: " |
| 194 "PTRACE_KILL waitpid returned %ld: %s"), |
| 195 (long) got_pid, strerror (errno)); |
| 196 return; |
| 197 } |
| 198 if (!WIFSIGNALED (kill_status)) |
| 199 { |
| 200 warning (_("linux_ptrace_test_ret_to_nx: " |
| 201 "PTRACE_KILL status %d is not WIFSIGNALED!"), |
| 202 status); |
| 203 return; |
| 204 } |
| 205 } |
| 206 |
| 207 /* + 1 is there as x86* stops after the 'int3' instruction. */ |
| 208 if (WSTOPSIG (status) == SIGTRAP && pc == return_address + 1) |
| 209 { |
| 210 /* PASS */ |
| 211 return; |
| 212 } |
| 213 |
| 214 /* We may get SIGSEGV due to missing PROT_EXEC of the RETURN_ADDRESS page. */ |
| 215 if (WSTOPSIG (status) == SIGSEGV && pc == return_address) |
| 216 { |
| 217 /* PASS */ |
| 218 return; |
| 219 } |
| 220 |
| 221 if ((void (*) (void)) pc != &linux_ptrace_test_ret_to_nx_instr) |
| 222 warning (_("linux_ptrace_test_ret_to_nx: PC %p is neither near return " |
| 223 "address %p nor is the return instruction %p!"), |
| 224 pc, return_address, &linux_ptrace_test_ret_to_nx_instr); |
| 225 else |
| 226 warning (_("Cannot call inferior functions, you have broken " |
| 227 "Linux kernel i386 NX (non-executable pages) support!")); |
| 228 #endif /* defined __i386__ || defined __x86_64__ */ |
| 229 } |
| 230 |
| 231 /* Display possible problems on this system. Display them only once per GDB |
| 232 execution. */ |
| 233 |
| 234 void |
| 235 linux_ptrace_init_warnings (void) |
| 236 { |
| 237 static int warned = 0; |
| 238 |
| 239 if (warned) |
| 240 return; |
| 241 warned = 1; |
| 242 |
| 243 linux_ptrace_test_ret_to_nx (); |
| 244 } |
OLD | NEW |