Blame view
arch/x86/kernel/ptrace.c
35 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 |
/* By Ross Biro 1/23/92 */ /* * Pentium III FXSR, SSE support * Gareth Hughes <gareth@valinux.com>, May 2000 */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/smp.h> |
1da177e4c Linux-2.6.12-rc2 |
11 |
#include <linux/errno.h> |
5a0e3ad6a include cleanup: ... |
12 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
13 |
#include <linux/ptrace.h> |
91e7b707a x86: x86 user_reg... |
14 |
#include <linux/regset.h> |
eeea3c3ff x86: tracehook sy... |
15 |
#include <linux/tracehook.h> |
1da177e4c Linux-2.6.12-rc2 |
16 |
#include <linux/user.h> |
070459d95 x86: x86 user_reg... |
17 |
#include <linux/elf.h> |
1da177e4c Linux-2.6.12-rc2 |
18 19 20 |
#include <linux/security.h> #include <linux/audit.h> #include <linux/seccomp.h> |
7ed20e1ad [PATCH] convert t... |
21 |
#include <linux/signal.h> |
24f1e32c6 hw-breakpoints: R... |
22 23 |
#include <linux/perf_event.h> #include <linux/hw_breakpoint.h> |
1da177e4c Linux-2.6.12-rc2 |
24 25 26 27 28 29 30 31 32 |
#include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/system.h> #include <asm/processor.h> #include <asm/i387.h> #include <asm/debugreg.h> #include <asm/ldt.h> #include <asm/desc.h> |
2047b08be x86: x86 ptrace g... |
33 34 |
#include <asm/prctl.h> #include <asm/proto.h> |
72f674d20 hw-breakpoints: m... |
35 |
#include <asm/hw_breakpoint.h> |
eee3af4a2 x86, ptrace: supp... |
36 |
|
070459d95 x86: x86 user_reg... |
37 |
#include "tls.h" |
1c569f026 tracing: Create g... |
38 39 |
#define CREATE_TRACE_POINTS #include <trace/events/syscalls.h> |
070459d95 x86: x86 user_reg... |
40 41 42 43 |
enum x86_regset { REGSET_GENERAL, REGSET_FP, REGSET_XFP, |
325af5fb1 x86: ioperm user_... |
44 |
REGSET_IOPERM64 = REGSET_XFP, |
5b3efd500 x86, ptrace: regs... |
45 |
REGSET_XSTATE, |
070459d95 x86: x86 user_reg... |
46 |
REGSET_TLS, |
325af5fb1 x86: ioperm user_... |
47 |
REGSET_IOPERM32, |
070459d95 x86: x86 user_reg... |
48 |
}; |
eee3af4a2 x86, ptrace: supp... |
49 |
|
b1cf540f0 x86: Add pt_regs ... |
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
struct pt_regs_offset { const char *name; int offset; }; #define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)} #define REG_OFFSET_END {.name = NULL, .offset = 0} static const struct pt_regs_offset regoffset_table[] = { #ifdef CONFIG_X86_64 REG_OFFSET_NAME(r15), REG_OFFSET_NAME(r14), REG_OFFSET_NAME(r13), REG_OFFSET_NAME(r12), REG_OFFSET_NAME(r11), REG_OFFSET_NAME(r10), REG_OFFSET_NAME(r9), REG_OFFSET_NAME(r8), #endif REG_OFFSET_NAME(bx), REG_OFFSET_NAME(cx), REG_OFFSET_NAME(dx), REG_OFFSET_NAME(si), REG_OFFSET_NAME(di), REG_OFFSET_NAME(bp), REG_OFFSET_NAME(ax), #ifdef CONFIG_X86_32 REG_OFFSET_NAME(ds), REG_OFFSET_NAME(es), REG_OFFSET_NAME(fs), REG_OFFSET_NAME(gs), #endif REG_OFFSET_NAME(orig_ax), REG_OFFSET_NAME(ip), REG_OFFSET_NAME(cs), REG_OFFSET_NAME(flags), REG_OFFSET_NAME(sp), REG_OFFSET_NAME(ss), REG_OFFSET_END, }; /** * regs_query_register_offset() - query register offset from its name * @name: the name of a register * * regs_query_register_offset() returns the offset of a register in struct * pt_regs from its name. If the name is invalid, this returns -EINVAL; */ int regs_query_register_offset(const char *name) { const struct pt_regs_offset *roff; for (roff = regoffset_table; roff->name != NULL; roff++) if (!strcmp(roff->name, name)) return roff->offset; return -EINVAL; } /** * regs_query_register_name() - query register name from its offset * @offset: the offset of a register in struct pt_regs. * * regs_query_register_name() returns the name of a register from its * offset in struct pt_regs. If the @offset is invalid, this returns NULL; */ const char *regs_query_register_name(unsigned int offset) { const struct pt_regs_offset *roff; for (roff = regoffset_table; roff->name != NULL; roff++) if (roff->offset == offset) return roff->name; return NULL; } static const int arg_offs_table[] = { #ifdef CONFIG_X86_32 [0] = offsetof(struct pt_regs, ax), [1] = offsetof(struct pt_regs, dx), [2] = offsetof(struct pt_regs, cx) #else /* CONFIG_X86_64 */ [0] = offsetof(struct pt_regs, di), [1] = offsetof(struct pt_regs, si), [2] = offsetof(struct pt_regs, dx), [3] = offsetof(struct pt_regs, cx), [4] = offsetof(struct pt_regs, r8), [5] = offsetof(struct pt_regs, r9) #endif }; |
eee3af4a2 x86, ptrace: supp... |
137 |
/* |
1da177e4c Linux-2.6.12-rc2 |
138 139 140 |
* does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ |
9f155b980 [PATCH] i386: PTR... |
141 142 |
/* * Determines which flags the user has access to [1 = access, 0 = no access]. |
9f155b980 [PATCH] i386: PTR... |
143 |
*/ |
e39c28914 x86: ptrace FLAG_... |
144 145 146 147 148 149 |
#define FLAG_MASK_32 ((unsigned long) \ (X86_EFLAGS_CF | X86_EFLAGS_PF | \ X86_EFLAGS_AF | X86_EFLAGS_ZF | \ X86_EFLAGS_SF | X86_EFLAGS_TF | \ X86_EFLAGS_DF | X86_EFLAGS_OF | \ X86_EFLAGS_RF | X86_EFLAGS_AC)) |
2047b08be x86: x86 ptrace g... |
150 151 152 153 154 155 156 157 158 |
/* * Determines whether a value may be installed in a segment register. */ static inline bool invalid_selector(u16 value) { return unlikely(value != 0 && (value & SEGMENT_RPL_MASK) != USER_RPL); } #ifdef CONFIG_X86_32 |
e39c28914 x86: ptrace FLAG_... |
159 |
#define FLAG_MASK FLAG_MASK_32 |
1da177e4c Linux-2.6.12-rc2 |
160 |
|
4fe702c7e X86_32: declare p... |
161 |
static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno) |
1da177e4c Linux-2.6.12-rc2 |
162 |
{ |
65ea5b034 x86: rename the s... |
163 |
BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); |
ccbeed3a0 x86: make lazy %g... |
164 |
return ®s->bx + (regno >> 2); |
1da177e4c Linux-2.6.12-rc2 |
165 |
} |
06ee1b687 x86: x86 ptrace g... |
166 |
static u16 get_segment_reg(struct task_struct *task, unsigned long offset) |
1da177e4c Linux-2.6.12-rc2 |
167 |
{ |
06ee1b687 x86: x86 ptrace g... |
168 169 170 171 172 173 174 |
/* * Returning the value truncates it to 16 bits. */ unsigned int retval; if (offset != offsetof(struct user_regs_struct, gs)) retval = *pt_regs_access(task_pt_regs(task), offset); else { |
06ee1b687 x86: x86 ptrace g... |
175 |
if (task == current) |
d9a89a26e x86: add %gs acce... |
176 177 178 |
retval = get_user_gs(task_pt_regs(task)); else retval = task_user_gs(task); |
06ee1b687 x86: x86 ptrace g... |
179 180 181 182 183 184 185 186 187 188 |
} return retval; } static int set_segment_reg(struct task_struct *task, unsigned long offset, u16 value) { /* * The value argument was already truncated to 16 bits. */ |
2047b08be x86: x86 ptrace g... |
189 |
if (invalid_selector(value)) |
06ee1b687 x86: x86 ptrace g... |
190 |
return -EIO; |
c63855d04 x86 ptrace: disal... |
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
/* * For %cs and %ss we cannot permit a null selector. * We can permit a bogus selector as long as it has USER_RPL. * Null selectors are fine for other segment registers, but * we will never get back to user mode with invalid %cs or %ss * and will take the trap in iret instead. Much code relies * on user_mode() to distinguish a user trap frame (which can * safely use invalid selectors) from a kernel trap frame. */ switch (offset) { case offsetof(struct user_regs_struct, cs): case offsetof(struct user_regs_struct, ss): if (unlikely(value == 0)) return -EIO; default: |
06ee1b687 x86: x86 ptrace g... |
207 |
*pt_regs_access(task_pt_regs(task), offset) = value; |
c63855d04 x86 ptrace: disal... |
208 209 210 |
break; case offsetof(struct user_regs_struct, gs): |
06ee1b687 x86: x86 ptrace g... |
211 |
if (task == current) |
d9a89a26e x86: add %gs acce... |
212 213 214 |
set_user_gs(task_pt_regs(task), value); else task_user_gs(task) = value; |
1da177e4c Linux-2.6.12-rc2 |
215 |
} |
06ee1b687 x86: x86 ptrace g... |
216 |
|
1da177e4c Linux-2.6.12-rc2 |
217 218 |
return 0; } |
2047b08be x86: x86 ptrace g... |
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
#else /* CONFIG_X86_64 */ #define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT) static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long offset) { BUILD_BUG_ON(offsetof(struct pt_regs, r15) != 0); return ®s->r15 + (offset / sizeof(regs->r15)); } static u16 get_segment_reg(struct task_struct *task, unsigned long offset) { /* * Returning the value truncates it to 16 bits. */ unsigned int seg; switch (offset) { case offsetof(struct user_regs_struct, fs): if (task == current) { /* Older gas can't assemble movq %?s,%r?? */ asm("movl %%fs,%0" : "=r" (seg)); return seg; } return task->thread.fsindex; case offsetof(struct user_regs_struct, gs): if (task == current) { asm("movl %%gs,%0" : "=r" (seg)); return seg; } return task->thread.gsindex; case offsetof(struct user_regs_struct, ds): if (task == current) { asm("movl %%ds,%0" : "=r" (seg)); return seg; } return task->thread.ds; case offsetof(struct user_regs_struct, es): if (task == current) { asm("movl %%es,%0" : "=r" (seg)); return seg; } return task->thread.es; case offsetof(struct user_regs_struct, cs): case offsetof(struct user_regs_struct, ss): break; } return *pt_regs_access(task_pt_regs(task), offset); } static int set_segment_reg(struct task_struct *task, unsigned long offset, u16 value) { /* * The value argument was already truncated to 16 bits. */ if (invalid_selector(value)) return -EIO; switch (offset) { case offsetof(struct user_regs_struct,fs): /* * If this is setting fs as for normal 64-bit use but * setting fs_base has implicitly changed it, leave it. */ if ((value == FS_TLS_SEL && task->thread.fsindex == 0 && task->thread.fs != 0) || (value == 0 && task->thread.fsindex == FS_TLS_SEL && task->thread.fs == 0)) break; task->thread.fsindex = value; if (task == current) loadsegment(fs, task->thread.fsindex); break; case offsetof(struct user_regs_struct,gs): /* * If this is setting gs as for normal 64-bit use but * setting gs_base has implicitly changed it, leave it. */ if ((value == GS_TLS_SEL && task->thread.gsindex == 0 && task->thread.gs != 0) || (value == 0 && task->thread.gsindex == GS_TLS_SEL && task->thread.gs == 0)) break; task->thread.gsindex = value; if (task == current) load_gs_index(task->thread.gsindex); break; case offsetof(struct user_regs_struct,ds): task->thread.ds = value; if (task == current) loadsegment(ds, task->thread.ds); break; case offsetof(struct user_regs_struct,es): task->thread.es = value; if (task == current) loadsegment(es, task->thread.es); break; /* * Can't actually change these in 64-bit mode. */ case offsetof(struct user_regs_struct,cs): |
c63855d04 x86 ptrace: disal... |
323 324 |
if (unlikely(value == 0)) return -EIO; |
2047b08be x86: x86 ptrace g... |
325 326 327 |
#ifdef CONFIG_IA32_EMULATION if (test_tsk_thread_flag(task, TIF_IA32)) task_pt_regs(task)->cs = value; |
2047b08be x86: x86 ptrace g... |
328 |
#endif |
cb757c41f x86: x86 ia32 ptr... |
329 |
break; |
2047b08be x86: x86 ptrace g... |
330 |
case offsetof(struct user_regs_struct,ss): |
c63855d04 x86 ptrace: disal... |
331 332 |
if (unlikely(value == 0)) return -EIO; |
2047b08be x86: x86 ptrace g... |
333 334 335 |
#ifdef CONFIG_IA32_EMULATION if (test_tsk_thread_flag(task, TIF_IA32)) task_pt_regs(task)->ss = value; |
2047b08be x86: x86 ptrace g... |
336 |
#endif |
cb757c41f x86: x86 ia32 ptr... |
337 |
break; |
2047b08be x86: x86 ptrace g... |
338 339 340 341 |
} return 0; } |
2047b08be x86: x86 ptrace g... |
342 |
#endif /* CONFIG_X86_32 */ |
06ee1b687 x86: x86 ptrace g... |
343 |
static unsigned long get_flags(struct task_struct *task) |
1da177e4c Linux-2.6.12-rc2 |
344 |
{ |
06ee1b687 x86: x86 ptrace g... |
345 346 347 348 349 350 351 |
unsigned long retval = task_pt_regs(task)->flags; /* * If the debugger set TF, hide it from the readout. */ if (test_tsk_thread_flag(task, TIF_FORCED_TF)) retval &= ~X86_EFLAGS_TF; |
1da177e4c Linux-2.6.12-rc2 |
352 |
|
1da177e4c Linux-2.6.12-rc2 |
353 354 |
return retval; } |
06ee1b687 x86: x86 ptrace g... |
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
static int set_flags(struct task_struct *task, unsigned long value) { struct pt_regs *regs = task_pt_regs(task); /* * If the user value contains TF, mark that * it was not "us" (the debugger) that set it. * If not, make sure it stays set if we had. */ if (value & X86_EFLAGS_TF) clear_tsk_thread_flag(task, TIF_FORCED_TF); else if (test_tsk_thread_flag(task, TIF_FORCED_TF)) value |= X86_EFLAGS_TF; regs->flags = (regs->flags & ~FLAG_MASK) | (value & FLAG_MASK); return 0; } static int putreg(struct task_struct *child, unsigned long offset, unsigned long value) { switch (offset) { case offsetof(struct user_regs_struct, cs): case offsetof(struct user_regs_struct, ds): case offsetof(struct user_regs_struct, es): case offsetof(struct user_regs_struct, fs): case offsetof(struct user_regs_struct, gs): case offsetof(struct user_regs_struct, ss): return set_segment_reg(child, offset, value); case offsetof(struct user_regs_struct, flags): return set_flags(child, value); |
2047b08be x86: x86 ptrace g... |
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
#ifdef CONFIG_X86_64 case offsetof(struct user_regs_struct,fs_base): if (value >= TASK_SIZE_OF(child)) return -EIO; /* * When changing the segment base, use do_arch_prctl * to set either thread.fs or thread.fsindex and the * corresponding GDT slot. */ if (child->thread.fs != value) return do_arch_prctl(child, ARCH_SET_FS, value); return 0; case offsetof(struct user_regs_struct,gs_base): /* * Exactly the same here as the %fs handling above. */ if (value >= TASK_SIZE_OF(child)) return -EIO; if (child->thread.gs != value) return do_arch_prctl(child, ARCH_SET_GS, value); return 0; #endif |
06ee1b687 x86: x86 ptrace g... |
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
} *pt_regs_access(task_pt_regs(child), offset) = value; return 0; } static unsigned long getreg(struct task_struct *task, unsigned long offset) { switch (offset) { case offsetof(struct user_regs_struct, cs): case offsetof(struct user_regs_struct, ds): case offsetof(struct user_regs_struct, es): case offsetof(struct user_regs_struct, fs): case offsetof(struct user_regs_struct, gs): case offsetof(struct user_regs_struct, ss): return get_segment_reg(task, offset); case offsetof(struct user_regs_struct, flags): return get_flags(task); |
2047b08be x86: x86 ptrace g... |
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
#ifdef CONFIG_X86_64 case offsetof(struct user_regs_struct, fs_base): { /* * do_arch_prctl may have used a GDT slot instead of * the MSR. To userland, it appears the same either * way, except the %fs segment selector might not be 0. */ unsigned int seg = task->thread.fsindex; if (task->thread.fs != 0) return task->thread.fs; if (task == current) asm("movl %%fs,%0" : "=r" (seg)); if (seg != FS_TLS_SEL) return 0; return get_desc_base(&task->thread.tls_array[FS_TLS]); } case offsetof(struct user_regs_struct, gs_base): { /* * Exactly the same here as the %fs handling above. */ unsigned int seg = task->thread.gsindex; if (task->thread.gs != 0) return task->thread.gs; if (task == current) asm("movl %%gs,%0" : "=r" (seg)); if (seg != GS_TLS_SEL) return 0; return get_desc_base(&task->thread.tls_array[GS_TLS]); } #endif |
06ee1b687 x86: x86 ptrace g... |
461 462 463 464 |
} return *pt_regs_access(task_pt_regs(task), offset); } |
91e7b707a x86: x86 user_reg... |
465 466 467 468 469 470 471 |
static int genregs_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { if (kbuf) { unsigned long *k = kbuf; |
04a1e62c2 x86/ptrace: make ... |
472 |
while (count >= sizeof(*k)) { |
91e7b707a x86: x86 user_reg... |
473 474 475 476 477 478 |
*k++ = getreg(target, pos); count -= sizeof(*k); pos += sizeof(*k); } } else { unsigned long __user *u = ubuf; |
04a1e62c2 x86/ptrace: make ... |
479 |
while (count >= sizeof(*u)) { |
91e7b707a x86: x86 user_reg... |
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
if (__put_user(getreg(target, pos), u++)) return -EFAULT; count -= sizeof(*u); pos += sizeof(*u); } } return 0; } static int genregs_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int ret = 0; if (kbuf) { const unsigned long *k = kbuf; |
04a1e62c2 x86/ptrace: make ... |
498 |
while (count >= sizeof(*k) && !ret) { |
91e7b707a x86: x86 user_reg... |
499 500 501 502 503 504 |
ret = putreg(target, pos, *k++); count -= sizeof(*k); pos += sizeof(*k); } } else { const unsigned long __user *u = ubuf; |
04a1e62c2 x86/ptrace: make ... |
505 |
while (count >= sizeof(*u) && !ret) { |
91e7b707a x86: x86 user_reg... |
506 507 508 509 510 511 512 513 514 515 516 |
unsigned long word; ret = __get_user(word, u++); if (ret) break; ret = putreg(target, pos, word); count -= sizeof(*u); pos += sizeof(*u); } } return ret; } |
a8b0ca17b perf: Remove the ... |
517 |
static void ptrace_triggered(struct perf_event *bp, |
b326e9560 hw-breakpoints: U... |
518 519 |
struct perf_sample_data *data, struct pt_regs *regs) |
d9771e8c5 x86: x86-32 ptrac... |
520 |
{ |
0f5340933 x86: x86-32 threa... |
521 |
int i; |
24f1e32c6 hw-breakpoints: R... |
522 |
struct thread_struct *thread = &(current->thread); |
0f5340933 x86: x86-32 threa... |
523 |
|
72f674d20 hw-breakpoints: m... |
524 525 526 527 |
/* * Store in the virtual DR6 register the fact that the breakpoint * was hit so the thread's debugger will see it. */ |
24f1e32c6 hw-breakpoints: R... |
528 529 |
for (i = 0; i < HBP_NUM; i++) { if (thread->ptrace_bps[i] == bp) |
72f674d20 hw-breakpoints: m... |
530 |
break; |
24f1e32c6 hw-breakpoints: R... |
531 |
} |
d9771e8c5 x86: x86-32 ptrac... |
532 |
|
72f674d20 hw-breakpoints: m... |
533 534 |
thread->debugreg6 |= (DR_TRAP0 << i); } |
d9771e8c5 x86: x86-32 ptrac... |
535 |
|
1da177e4c Linux-2.6.12-rc2 |
536 |
/* |
24f1e32c6 hw-breakpoints: R... |
537 538 539 |
* Walk through every ptrace breakpoints for this thread and * build the dr7 value on top of their attributes. * |
d9771e8c5 x86: x86-32 ptrac... |
540 |
*/ |
24f1e32c6 hw-breakpoints: R... |
541 |
static unsigned long ptrace_get_dr7(struct perf_event *bp[]) |
d9771e8c5 x86: x86-32 ptrac... |
542 |
{ |
24f1e32c6 hw-breakpoints: R... |
543 544 545 546 547 548 549 550 551 |
int i; int dr7 = 0; struct arch_hw_breakpoint *info; for (i = 0; i < HBP_NUM; i++) { if (bp[i] && !bp[i]->attr.disabled) { info = counter_arch_bp(bp[i]); dr7 |= encode_dr7(i, info->len, info->type); } |
0f5340933 x86: x86-32 threa... |
552 |
} |
24f1e32c6 hw-breakpoints: R... |
553 554 |
return dr7; |
d9771e8c5 x86: x86-32 ptrac... |
555 |
} |
44234adcd hw-breakpoints: M... |
556 |
static int |
5fa10b28e hw-breakpoints: U... |
557 |
ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, |
1cedae729 hw-breakpoints: K... |
558 |
struct task_struct *tsk, int disabled) |
5fa10b28e hw-breakpoints: U... |
559 560 561 |
{ int err; int gen_len, gen_type; |
b326e9560 hw-breakpoints: U... |
562 |
struct perf_event_attr attr; |
5fa10b28e hw-breakpoints: U... |
563 564 |
/* |
c9404c9c3 Fix misspelling o... |
565 |
* We should have at least an inactive breakpoint at this |
5fa10b28e hw-breakpoints: U... |
566 567 568 569 |
* slot. It means the user is writing dr7 without having * written the address register first */ if (!bp) |
44234adcd hw-breakpoints: M... |
570 |
return -EINVAL; |
5fa10b28e hw-breakpoints: U... |
571 572 573 |
err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); if (err) |
44234adcd hw-breakpoints: M... |
574 |
return err; |
5fa10b28e hw-breakpoints: U... |
575 576 577 578 |
attr = bp->attr; attr.bp_len = gen_len; attr.bp_type = gen_type; |
1cedae729 hw-breakpoints: K... |
579 |
attr.disabled = disabled; |
5fa10b28e hw-breakpoints: U... |
580 |
|
2f0993e0f hw-breakpoints: D... |
581 |
return modify_user_hw_breakpoint(bp, &attr); |
5fa10b28e hw-breakpoints: U... |
582 |
} |
24f1e32c6 hw-breakpoints: R... |
583 |
/* |
72f674d20 hw-breakpoints: m... |
584 585 586 |
* Handle ptrace writes to debug register 7. */ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) |
d9771e8c5 x86: x86-32 ptrac... |
587 |
{ |
72f674d20 hw-breakpoints: m... |
588 |
struct thread_struct *thread = &(tsk->thread); |
24f1e32c6 hw-breakpoints: R... |
589 |
unsigned long old_dr7; |
72f674d20 hw-breakpoints: m... |
590 591 592 |
int i, orig_ret = 0, rc = 0; int enabled, second_pass = 0; unsigned len, type; |
24f1e32c6 hw-breakpoints: R... |
593 |
struct perf_event *bp; |
72f674d20 hw-breakpoints: m... |
594 |
|
87dc669ba x86, hw_breakpoin... |
595 596 |
if (ptrace_get_breakpoints(tsk) < 0) return -ESRCH; |
72f674d20 hw-breakpoints: m... |
597 |
data &= ~DR_CONTROL_RESERVED; |
24f1e32c6 hw-breakpoints: R... |
598 |
old_dr7 = ptrace_get_dr7(thread->ptrace_bps); |
72f674d20 hw-breakpoints: m... |
599 600 601 602 603 604 605 |
restore: /* * Loop through all the hardware breakpoints, making the * appropriate changes to each. */ for (i = 0; i < HBP_NUM; i++) { enabled = decode_dr7(data, i, &len, &type); |
24f1e32c6 hw-breakpoints: R... |
606 |
bp = thread->ptrace_bps[i]; |
72f674d20 hw-breakpoints: m... |
607 608 609 |
if (!enabled) { if (bp) { |
24f1e32c6 hw-breakpoints: R... |
610 611 |
/* * Don't unregister the breakpoints right-away, |
72f674d20 hw-breakpoints: m... |
612 613 614 615 616 617 618 |
* unless all register_user_hw_breakpoint() * requests have succeeded. This prevents * any window of opportunity for debug * register grabbing by other users. */ if (!second_pass) continue; |
1cedae729 hw-breakpoints: K... |
619 |
|
44234adcd hw-breakpoints: M... |
620 |
rc = ptrace_modify_breakpoint(bp, len, type, |
1cedae729 hw-breakpoints: K... |
621 |
tsk, 1); |
44234adcd hw-breakpoints: M... |
622 |
if (rc) |
1cedae729 hw-breakpoints: K... |
623 |
break; |
72f674d20 hw-breakpoints: m... |
624 625 626 |
} continue; } |
0f5340933 x86: x86-32 threa... |
627 |
|
44234adcd hw-breakpoints: M... |
628 629 |
rc = ptrace_modify_breakpoint(bp, len, type, tsk, 0); if (rc) |
24f1e32c6 hw-breakpoints: R... |
630 |
break; |
72f674d20 hw-breakpoints: m... |
631 632 633 634 635 636 637 638 639 640 641 642 643 |
} /* * Make a second pass to free the remaining unused breakpoints * or to restore the original breakpoints if an error occurred. */ if (!second_pass) { second_pass = 1; if (rc < 0) { orig_ret = rc; data = old_dr7; } goto restore; } |
87dc669ba x86, hw_breakpoin... |
644 645 |
ptrace_put_breakpoints(tsk); |
72f674d20 hw-breakpoints: m... |
646 647 |
return ((orig_ret < 0) ? orig_ret : rc); } |
0f5340933 x86: x86-32 threa... |
648 |
|
72f674d20 hw-breakpoints: m... |
649 650 651 |
/* * Handle PTRACE_PEEKUSR calls for the debug register area. */ |
9d22b5366 x86: Mark ptrace_... |
652 |
static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n) |
72f674d20 hw-breakpoints: m... |
653 654 655 |
{ struct thread_struct *thread = &(tsk->thread); unsigned long val = 0; |
24f1e32c6 hw-breakpoints: R... |
656 657 |
if (n < HBP_NUM) { struct perf_event *bp; |
87dc669ba x86, hw_breakpoin... |
658 659 660 |
if (ptrace_get_breakpoints(tsk) < 0) return -ESRCH; |
24f1e32c6 hw-breakpoints: R... |
661 662 |
bp = thread->ptrace_bps[n]; if (!bp) |
87dc669ba x86, hw_breakpoin... |
663 664 665 666 667 |
val = 0; else val = bp->hw.info.address; ptrace_put_breakpoints(tsk); |
24f1e32c6 hw-breakpoints: R... |
668 |
} else if (n == 6) { |
72f674d20 hw-breakpoints: m... |
669 |
val = thread->debugreg6; |
24f1e32c6 hw-breakpoints: R... |
670 |
} else if (n == 7) { |
326264a02 hw-breakpoint: Ke... |
671 |
val = thread->ptrace_dr7; |
24f1e32c6 hw-breakpoints: R... |
672 |
} |
72f674d20 hw-breakpoints: m... |
673 674 |
return val; } |
0f5340933 x86: x86-32 threa... |
675 |
|
24f1e32c6 hw-breakpoints: R... |
676 677 678 679 680 |
static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, unsigned long addr) { struct perf_event *bp; struct thread_struct *t = &tsk->thread; |
b326e9560 hw-breakpoints: U... |
681 |
struct perf_event_attr attr; |
87dc669ba x86, hw_breakpoin... |
682 683 684 685 |
int err = 0; if (ptrace_get_breakpoints(tsk) < 0) return -ESRCH; |
24f1e32c6 hw-breakpoints: R... |
686 687 |
if (!t->ptrace_bps[nr]) { |
73266fc1d hw-breakpoints: T... |
688 |
ptrace_breakpoint_init(&attr); |
d9771e8c5 x86: x86-32 ptrac... |
689 |
/* |
24f1e32c6 hw-breakpoints: R... |
690 691 |
* Put stub len and type to register (reserve) an inactive but * correct bp |
d9771e8c5 x86: x86-32 ptrac... |
692 |
*/ |
5fa10b28e hw-breakpoints: U... |
693 694 695 696 |
attr.bp_addr = addr; attr.bp_len = HW_BREAKPOINT_LEN_1; attr.bp_type = HW_BREAKPOINT_W; attr.disabled = 1; |
4dc0da869 perf: Add context... |
697 698 |
bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, tsk); |
44234adcd hw-breakpoints: M... |
699 700 701 702 703 704 705 706 707 708 |
/* * CHECKME: the previous code returned -EIO if the addr wasn't * a valid task virtual addr. The new one will return -EINVAL in * this case. * -EINVAL may be what we want for in-kernel breakpoints users, * but -EIO looks better for ptrace, since we refuse a register * writing for the user. And anyway this is the previous * behaviour. */ |
87dc669ba x86, hw_breakpoin... |
709 710 711 712 |
if (IS_ERR(bp)) { err = PTR_ERR(bp); goto put; } |
44234adcd hw-breakpoints: M... |
713 714 |
t->ptrace_bps[nr] = bp; |
24f1e32c6 hw-breakpoints: R... |
715 716 |
} else { bp = t->ptrace_bps[nr]; |
5fa10b28e hw-breakpoints: U... |
717 718 719 |
attr = bp->attr; attr.bp_addr = addr; |
44234adcd hw-breakpoints: M... |
720 |
err = modify_user_hw_breakpoint(bp, &attr); |
d9771e8c5 x86: x86-32 ptrac... |
721 |
} |
24f1e32c6 hw-breakpoints: R... |
722 |
|
87dc669ba x86, hw_breakpoin... |
723 724 725 |
put: ptrace_put_breakpoints(tsk); return err; |
d9771e8c5 x86: x86-32 ptrac... |
726 |
} |
325af5fb1 x86: ioperm user_... |
727 |
/* |
72f674d20 hw-breakpoints: m... |
728 729 |
* Handle PTRACE_POKEUSR calls for the debug register area. */ |
98b8b99ae arch/x86/kernel/p... |
730 731 |
static int ptrace_set_debugreg(struct task_struct *tsk, int n, unsigned long val) |
72f674d20 hw-breakpoints: m... |
732 733 734 735 736 737 738 739 740 |
{ struct thread_struct *thread = &(tsk->thread); int rc = 0; /* There are no DR4 or DR5 registers */ if (n == 4 || n == 5) return -EIO; if (n == 6) { |
24f1e32c6 hw-breakpoints: R... |
741 |
thread->debugreg6 = val; |
72f674d20 hw-breakpoints: m... |
742 |
goto ret_path; |
d9771e8c5 x86: x86-32 ptrac... |
743 |
} |
72f674d20 hw-breakpoints: m... |
744 |
if (n < HBP_NUM) { |
24f1e32c6 hw-breakpoints: R... |
745 746 747 |
rc = ptrace_set_breakpoint_addr(tsk, n, val); if (rc) return rc; |
72f674d20 hw-breakpoints: m... |
748 749 |
} /* All that's left is DR7 */ |
326264a02 hw-breakpoint: Ke... |
750 |
if (n == 7) { |
72f674d20 hw-breakpoints: m... |
751 |
rc = ptrace_write_dr7(tsk, val); |
326264a02 hw-breakpoint: Ke... |
752 753 754 |
if (!rc) thread->ptrace_dr7 = val; } |
d9771e8c5 x86: x86-32 ptrac... |
755 |
|
72f674d20 hw-breakpoints: m... |
756 757 |
ret_path: return rc; |
d9771e8c5 x86: x86-32 ptrac... |
758 |
} |
325af5fb1 x86: ioperm user_... |
759 760 761 762 763 764 765 766 767 |
/* * These access the current or another (stopped) task's io permission * bitmap for debugging or core dump. */ static int ioperm_active(struct task_struct *target, const struct user_regset *regset) { return target->thread.io_bitmap_max / regset->size; } |
b4ef95de0 x86: disable BTS ... |
768 |
|
325af5fb1 x86: ioperm user_... |
769 770 771 772 |
static int ioperm_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) |
eee3af4a2 x86, ptrace: supp... |
773 |
{ |
325af5fb1 x86: ioperm user_... |
774 |
if (!target->thread.io_bitmap_ptr) |
eee3af4a2 x86, ptrace: supp... |
775 |
return -ENXIO; |
325af5fb1 x86: ioperm user_... |
776 777 778 779 |
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, target->thread.io_bitmap_ptr, 0, IO_BITMAP_BYTES); } |
d9771e8c5 x86: x86-32 ptrac... |
780 |
/* |
1da177e4c Linux-2.6.12-rc2 |
781 782 783 784 785 |
* Called by kernel/ptrace.c when detaching.. * * Make sure the single step bit is not set. */ void ptrace_disable(struct task_struct *child) |
9e714bed6 x86: x86-32 ptrac... |
786 |
{ |
7f232343e x86: arch_has_sin... |
787 |
user_disable_single_step(child); |
e9c86c789 x86: x86 ptrace a... |
788 |
#ifdef TIF_SYSCALL_EMU |
ab1c23c24 [PATCH] SYSEMU: f... |
789 |
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); |
e9c86c789 x86: x86 ptrace a... |
790 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
791 |
} |
5a4646a4e x86: x86 ptrace u... |
792 793 794 |
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION static const struct user_regset_view user_x86_32_view; /* Initialized below. */ #endif |
9b05a69e0 ptrace: change si... |
795 796 |
long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) |
1da177e4c Linux-2.6.12-rc2 |
797 |
{ |
5a4646a4e x86: x86 ptrace u... |
798 |
int ret; |
1da177e4c Linux-2.6.12-rc2 |
799 |
unsigned long __user *datap = (unsigned long __user *)data; |
1da177e4c Linux-2.6.12-rc2 |
800 |
switch (request) { |
1da177e4c Linux-2.6.12-rc2 |
801 802 803 804 805 |
/* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; ret = -EIO; |
eb5a36993 ptrace: cleanup a... |
806 |
if ((addr & (sizeof(data) - 1)) || addr >= sizeof(struct user)) |
1da177e4c Linux-2.6.12-rc2 |
807 808 809 |
break; tmp = 0; /* Default return condition */ |
e9c86c789 x86: x86 ptrace a... |
810 |
if (addr < sizeof(struct user_regs_struct)) |
1da177e4c Linux-2.6.12-rc2 |
811 |
tmp = getreg(child, addr); |
e9c86c789 x86: x86 ptrace a... |
812 813 814 815 |
else if (addr >= offsetof(struct user, u_debugreg[0]) && addr <= offsetof(struct user, u_debugreg[7])) { addr -= offsetof(struct user, u_debugreg[0]); tmp = ptrace_get_debugreg(child, addr / sizeof(data)); |
1da177e4c Linux-2.6.12-rc2 |
816 817 818 819 |
} ret = put_user(tmp, datap); break; } |
1da177e4c Linux-2.6.12-rc2 |
820 821 |
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret = -EIO; |
eb5a36993 ptrace: cleanup a... |
822 |
if ((addr & (sizeof(data) - 1)) || addr >= sizeof(struct user)) |
1da177e4c Linux-2.6.12-rc2 |
823 |
break; |
e9c86c789 x86: x86 ptrace a... |
824 |
if (addr < sizeof(struct user_regs_struct)) |
1da177e4c Linux-2.6.12-rc2 |
825 |
ret = putreg(child, addr, data); |
e9c86c789 x86: x86 ptrace a... |
826 827 828 829 830 |
else if (addr >= offsetof(struct user, u_debugreg[0]) && addr <= offsetof(struct user, u_debugreg[7])) { addr -= offsetof(struct user, u_debugreg[0]); ret = ptrace_set_debugreg(child, addr / sizeof(data), data); |
1da177e4c Linux-2.6.12-rc2 |
831 |
} |
e9c86c789 x86: x86 ptrace a... |
832 |
break; |
1da177e4c Linux-2.6.12-rc2 |
833 |
|
5a4646a4e x86: x86 ptrace u... |
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 |
case PTRACE_GETREGS: /* Get all gp regs from the child. */ return copy_regset_to_user(child, task_user_regset_view(current), REGSET_GENERAL, 0, sizeof(struct user_regs_struct), datap); case PTRACE_SETREGS: /* Set all gp regs in the child. */ return copy_regset_from_user(child, task_user_regset_view(current), REGSET_GENERAL, 0, sizeof(struct user_regs_struct), datap); case PTRACE_GETFPREGS: /* Get the child FPU state. */ return copy_regset_to_user(child, task_user_regset_view(current), REGSET_FP, 0, sizeof(struct user_i387_struct), datap); case PTRACE_SETFPREGS: /* Set the child FPU state. */ return copy_regset_from_user(child, task_user_regset_view(current), REGSET_FP, 0, sizeof(struct user_i387_struct), datap); |
1da177e4c Linux-2.6.12-rc2 |
861 |
|
e9c86c789 x86: x86 ptrace a... |
862 |
#ifdef CONFIG_X86_32 |
5a4646a4e x86: x86 ptrace u... |
863 864 865 866 |
case PTRACE_GETFPXREGS: /* Get the child extended FPU state. */ return copy_regset_to_user(child, &user_x86_32_view, REGSET_XFP, 0, sizeof(struct user_fxsr_struct), |
45fdc3a76 x86 ptrace: fix P... |
867 |
datap) ? -EIO : 0; |
5a4646a4e x86: x86 ptrace u... |
868 869 870 871 872 |
case PTRACE_SETFPXREGS: /* Set the child extended FPU state. */ return copy_regset_from_user(child, &user_x86_32_view, REGSET_XFP, 0, sizeof(struct user_fxsr_struct), |
45fdc3a76 x86 ptrace: fix P... |
873 |
datap) ? -EIO : 0; |
e9c86c789 x86: x86 ptrace a... |
874 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
875 |
|
e9c86c789 x86: x86 ptrace a... |
876 |
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
1da177e4c Linux-2.6.12-rc2 |
877 |
case PTRACE_GET_THREAD_AREA: |
9b05a69e0 ptrace: change si... |
878 |
if ((int) addr < 0) |
efd1ca52d x86: TLS cleanup |
879 880 |
return -EIO; ret = do_get_thread_area(child, addr, |
eb5a36993 ptrace: cleanup a... |
881 |
(struct user_desc __user *)data); |
1da177e4c Linux-2.6.12-rc2 |
882 883 884 |
break; case PTRACE_SET_THREAD_AREA: |
9b05a69e0 ptrace: change si... |
885 |
if ((int) addr < 0) |
efd1ca52d x86: TLS cleanup |
886 887 |
return -EIO; ret = do_set_thread_area(child, addr, |
eb5a36993 ptrace: cleanup a... |
888 |
(struct user_desc __user *)data, 0); |
1da177e4c Linux-2.6.12-rc2 |
889 |
break; |
e9c86c789 x86: x86 ptrace a... |
890 891 892 893 894 895 896 897 898 899 |
#endif #ifdef CONFIG_X86_64 /* normal 64bit interface to access TLS data. Works just like arch_prctl, except that the arguments are reversed. */ case PTRACE_ARCH_PRCTL: ret = do_arch_prctl(child, data, addr); break; #endif |
1da177e4c Linux-2.6.12-rc2 |
900 901 902 903 904 |
default: ret = ptrace_request(child, request, addr, data); break; } |
d9771e8c5 x86: x86-32 ptrac... |
905 |
|
1da177e4c Linux-2.6.12-rc2 |
906 907 |
return ret; } |
cb757c41f x86: x86 ia32 ptr... |
908 |
#ifdef CONFIG_IA32_EMULATION |
099cd6e9d x86: x86 ia32 ptr... |
909 910 911 |
#include <linux/compat.h> #include <linux/syscalls.h> #include <asm/ia32.h> |
cb757c41f x86: x86 ia32 ptr... |
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 |
#include <asm/user32.h> #define R32(l,q) \ case offsetof(struct user32, regs.l): \ regs->q = value; break #define SEG32(rs) \ case offsetof(struct user32, regs.rs): \ return set_segment_reg(child, \ offsetof(struct user_regs_struct, rs), \ value); \ break static int putreg32(struct task_struct *child, unsigned regno, u32 value) { struct pt_regs *regs = task_pt_regs(child); switch (regno) { SEG32(cs); SEG32(ds); SEG32(es); SEG32(fs); SEG32(gs); SEG32(ss); R32(ebx, bx); R32(ecx, cx); R32(edx, dx); R32(edi, di); R32(esi, si); R32(ebp, bp); R32(eax, ax); |
cb757c41f x86: x86 ia32 ptr... |
945 946 |
R32(eip, ip); R32(esp, sp); |
40f0933d5 x86: ia32 syscall... |
947 948 |
case offsetof(struct user32, regs.orig_eax): /* |
8cb3ed139 x86: ptrace: set ... |
949 950 951 952 953 |
* A 32-bit debugger setting orig_eax means to restore * the state of the task restarting a 32-bit syscall. * Make sure we interpret the -ERESTART* codes correctly * in case the task is not actually still sitting at the * exit from a 32-bit syscall with TS_COMPAT still set. |
40f0933d5 x86: ia32 syscall... |
954 |
*/ |
8cb3ed139 x86: ptrace: set ... |
955 956 957 |
regs->orig_ax = value; if (syscall_get_nr(child, regs) >= 0) task_thread_info(child)->status |= TS_COMPAT; |
40f0933d5 x86: ia32 syscall... |
958 |
break; |
cb757c41f x86: x86 ia32 ptr... |
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 |
case offsetof(struct user32, regs.eflags): return set_flags(child, value); case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[7]): regno -= offsetof(struct user32, u_debugreg[0]); return ptrace_set_debugreg(child, regno / 4, value); default: if (regno > sizeof(struct user32) || (regno & 3)) return -EIO; /* * Other dummy fields in the virtual user structure * are ignored */ break; } return 0; } #undef R32 #undef SEG32 #define R32(l,q) \ case offsetof(struct user32, regs.l): \ *val = regs->q; break #define SEG32(rs) \ case offsetof(struct user32, regs.rs): \ *val = get_segment_reg(child, \ offsetof(struct user_regs_struct, rs)); \ break static int getreg32(struct task_struct *child, unsigned regno, u32 *val) { struct pt_regs *regs = task_pt_regs(child); switch (regno) { SEG32(ds); SEG32(es); SEG32(fs); SEG32(gs); R32(cs, cs); R32(ss, ss); R32(ebx, bx); R32(ecx, cx); R32(edx, dx); R32(edi, di); R32(esi, si); R32(ebp, bp); R32(eax, ax); R32(orig_eax, orig_ax); R32(eip, ip); R32(esp, sp); case offsetof(struct user32, regs.eflags): *val = get_flags(child); break; case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[7]): regno -= offsetof(struct user32, u_debugreg[0]); *val = ptrace_get_debugreg(child, regno / 4); break; default: if (regno > sizeof(struct user32) || (regno & 3)) return -EIO; /* * Other dummy fields in the virtual user structure * are ignored */ *val = 0; break; } return 0; } #undef R32 #undef SEG32 |
91e7b707a x86: x86 user_reg... |
1043 1044 1045 1046 1047 1048 1049 |
static int genregs32_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { if (kbuf) { compat_ulong_t *k = kbuf; |
04a1e62c2 x86/ptrace: make ... |
1050 |
while (count >= sizeof(*k)) { |
91e7b707a x86: x86 user_reg... |
1051 1052 1053 1054 1055 1056 |
getreg32(target, pos, k++); count -= sizeof(*k); pos += sizeof(*k); } } else { compat_ulong_t __user *u = ubuf; |
04a1e62c2 x86/ptrace: make ... |
1057 |
while (count >= sizeof(*u)) { |
91e7b707a x86: x86 user_reg... |
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 |
compat_ulong_t word; getreg32(target, pos, &word); if (__put_user(word, u++)) return -EFAULT; count -= sizeof(*u); pos += sizeof(*u); } } return 0; } static int genregs32_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int ret = 0; if (kbuf) { const compat_ulong_t *k = kbuf; |
04a1e62c2 x86/ptrace: make ... |
1078 |
while (count >= sizeof(*k) && !ret) { |
f9cb02b0b x86 ptrace: fix c... |
1079 |
ret = putreg32(target, pos, *k++); |
91e7b707a x86: x86 user_reg... |
1080 1081 1082 1083 1084 |
count -= sizeof(*k); pos += sizeof(*k); } } else { const compat_ulong_t __user *u = ubuf; |
04a1e62c2 x86/ptrace: make ... |
1085 |
while (count >= sizeof(*u) && !ret) { |
91e7b707a x86: x86 user_reg... |
1086 1087 1088 1089 |
compat_ulong_t word; ret = __get_user(word, u++); if (ret) break; |
f9cb02b0b x86 ptrace: fix c... |
1090 |
ret = putreg32(target, pos, word); |
91e7b707a x86: x86 user_reg... |
1091 1092 1093 1094 1095 1096 |
count -= sizeof(*u); pos += sizeof(*u); } } return ret; } |
562b80baf x86_64 ia32 ptrac... |
1097 1098 |
long compat_arch_ptrace(struct task_struct *child, compat_long_t request, compat_ulong_t caddr, compat_ulong_t cdata) |
099cd6e9d x86: x86 ia32 ptr... |
1099 |
{ |
562b80baf x86_64 ia32 ptrac... |
1100 1101 |
unsigned long addr = caddr; unsigned long data = cdata; |
099cd6e9d x86: x86 ia32 ptr... |
1102 1103 1104 1105 1106 |
void __user *datap = compat_ptr(data); int ret; __u32 val; switch (request) { |
099cd6e9d x86: x86 ia32 ptr... |
1107 1108 1109 1110 1111 1112 1113 1114 1115 |
case PTRACE_PEEKUSR: ret = getreg32(child, addr, &val); if (ret == 0) ret = put_user(val, (__u32 __user *)datap); break; case PTRACE_POKEUSR: ret = putreg32(child, addr, data); break; |
5a4646a4e x86: x86 ptrace u... |
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 |
case PTRACE_GETREGS: /* Get all gp regs from the child. */ return copy_regset_to_user(child, &user_x86_32_view, REGSET_GENERAL, 0, sizeof(struct user_regs_struct32), datap); case PTRACE_SETREGS: /* Set all gp regs in the child. */ return copy_regset_from_user(child, &user_x86_32_view, REGSET_GENERAL, 0, sizeof(struct user_regs_struct32), datap); case PTRACE_GETFPREGS: /* Get the child FPU state. */ return copy_regset_to_user(child, &user_x86_32_view, REGSET_FP, 0, sizeof(struct user_i387_ia32_struct), datap); case PTRACE_SETFPREGS: /* Set the child FPU state. */ return copy_regset_from_user( child, &user_x86_32_view, REGSET_FP, 0, sizeof(struct user_i387_ia32_struct), datap); case PTRACE_GETFPXREGS: /* Get the child extended FPU state. */ return copy_regset_to_user(child, &user_x86_32_view, REGSET_XFP, 0, sizeof(struct user32_fxsr_struct), datap); case PTRACE_SETFPXREGS: /* Set the child extended FPU state. */ return copy_regset_from_user(child, &user_x86_32_view, REGSET_XFP, 0, sizeof(struct user32_fxsr_struct), datap); |
099cd6e9d x86: x86 ia32 ptr... |
1150 |
|
562b80baf x86_64 ia32 ptrac... |
1151 1152 1153 |
case PTRACE_GET_THREAD_AREA: case PTRACE_SET_THREAD_AREA: return arch_ptrace(child, request, addr, data); |
099cd6e9d x86: x86 ia32 ptr... |
1154 |
default: |
fdadd54db x86: x86 ptrace g... |
1155 |
return compat_ptrace_request(child, request, addr, data); |
099cd6e9d x86: x86 ia32 ptr... |
1156 |
} |
099cd6e9d x86: x86 ia32 ptr... |
1157 1158 |
return ret; } |
cb757c41f x86: x86 ia32 ptr... |
1159 |
#endif /* CONFIG_IA32_EMULATION */ |
070459d95 x86: x86 user_reg... |
1160 |
#ifdef CONFIG_X86_64 |
5b3efd500 x86, ptrace: regs... |
1161 |
static struct user_regset x86_64_regsets[] __read_mostly = { |
070459d95 x86: x86 user_reg... |
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 |
[REGSET_GENERAL] = { .core_note_type = NT_PRSTATUS, .n = sizeof(struct user_regs_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), .get = genregs_get, .set = genregs_set }, [REGSET_FP] = { .core_note_type = NT_PRFPREG, .n = sizeof(struct user_i387_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set }, |
5b3efd500 x86, ptrace: regs... |
1174 1175 1176 1177 1178 1179 |
[REGSET_XSTATE] = { .core_note_type = NT_X86_XSTATE, .size = sizeof(u64), .align = sizeof(u64), .active = xstateregs_active, .get = xstateregs_get, .set = xstateregs_set }, |
325af5fb1 x86: ioperm user_... |
1180 1181 1182 1183 1184 1185 |
[REGSET_IOPERM64] = { .core_note_type = NT_386_IOPERM, .n = IO_BITMAP_LONGS, .size = sizeof(long), .align = sizeof(long), .active = ioperm_active, .get = ioperm_get }, |
070459d95 x86: x86 user_reg... |
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 |
}; static const struct user_regset_view user_x86_64_view = { .name = "x86_64", .e_machine = EM_X86_64, .regsets = x86_64_regsets, .n = ARRAY_SIZE(x86_64_regsets) }; #else /* CONFIG_X86_32 */ #define user_regs_struct32 user_regs_struct #define genregs32_get genregs_get #define genregs32_set genregs_set |
1f465f4e4 x86: user_regset_... |
1198 1199 |
#define user_i387_ia32_struct user_i387_struct #define user32_fxsr_struct user_fxsr_struct |
070459d95 x86: x86 user_reg... |
1200 1201 1202 |
#endif /* CONFIG_X86_64 */ #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
5b3efd500 x86, ptrace: regs... |
1203 |
static struct user_regset x86_32_regsets[] __read_mostly = { |
070459d95 x86: x86 user_reg... |
1204 1205 1206 1207 1208 1209 1210 1211 |
[REGSET_GENERAL] = { .core_note_type = NT_PRSTATUS, .n = sizeof(struct user_regs_struct32) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), .get = genregs32_get, .set = genregs32_set }, [REGSET_FP] = { .core_note_type = NT_PRFPREG, |
1f465f4e4 x86: user_regset_... |
1212 |
.n = sizeof(struct user_i387_ia32_struct) / sizeof(u32), |
070459d95 x86: x86 user_reg... |
1213 1214 1215 1216 1217 |
.size = sizeof(u32), .align = sizeof(u32), .active = fpregs_active, .get = fpregs_get, .set = fpregs_set }, [REGSET_XFP] = { .core_note_type = NT_PRXFPREG, |
1f465f4e4 x86: user_regset_... |
1218 |
.n = sizeof(struct user32_fxsr_struct) / sizeof(u32), |
070459d95 x86: x86 user_reg... |
1219 1220 1221 |
.size = sizeof(u32), .align = sizeof(u32), .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set }, |
5b3efd500 x86, ptrace: regs... |
1222 1223 1224 1225 1226 1227 |
[REGSET_XSTATE] = { .core_note_type = NT_X86_XSTATE, .size = sizeof(u64), .align = sizeof(u64), .active = xstateregs_active, .get = xstateregs_get, .set = xstateregs_set }, |
070459d95 x86: x86 user_reg... |
1228 |
[REGSET_TLS] = { |
bb61682b3 x86: x86 core dum... |
1229 |
.core_note_type = NT_386_TLS, |
070459d95 x86: x86 user_reg... |
1230 1231 1232 1233 1234 1235 |
.n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN, .size = sizeof(struct user_desc), .align = sizeof(struct user_desc), .active = regset_tls_active, .get = regset_tls_get, .set = regset_tls_set }, |
325af5fb1 x86: ioperm user_... |
1236 1237 1238 1239 1240 1241 |
[REGSET_IOPERM32] = { .core_note_type = NT_386_IOPERM, .n = IO_BITMAP_BYTES / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), .active = ioperm_active, .get = ioperm_get }, |
070459d95 x86: x86 user_reg... |
1242 1243 1244 1245 1246 1247 1248 |
}; static const struct user_regset_view user_x86_32_view = { .name = "i386", .e_machine = EM_386, .regsets = x86_32_regsets, .n = ARRAY_SIZE(x86_32_regsets) }; #endif |
5b3efd500 x86, ptrace: regs... |
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 |
/* * This represents bytes 464..511 in the memory layout exported through * the REGSET_XSTATE interface. */ u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS]; void update_regset_xstate_info(unsigned int size, u64 xstate_mask) { #ifdef CONFIG_X86_64 x86_64_regsets[REGSET_XSTATE].n = size / sizeof(u64); #endif #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION x86_32_regsets[REGSET_XSTATE].n = size / sizeof(u64); #endif xstate_fx_sw_bytes[USER_XSTATE_XCR0_WORD] = xstate_mask; } |
070459d95 x86: x86 user_reg... |
1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 |
const struct user_regset_view *task_user_regset_view(struct task_struct *task) { #ifdef CONFIG_IA32_EMULATION if (test_tsk_thread_flag(task, TIF_IA32)) #endif #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION return &user_x86_32_view; #endif #ifdef CONFIG_X86_64 return &user_x86_64_view; #endif } |
7f38551fc ptrace: x86: impl... |
1277 1278 1279 1280 |
static void fill_sigtrap_info(struct task_struct *tsk, struct pt_regs *regs, int error_code, int si_code, struct siginfo *info) |
1da177e4c Linux-2.6.12-rc2 |
1281 |
{ |
1da177e4c Linux-2.6.12-rc2 |
1282 1283 |
tsk->thread.trap_no = 1; tsk->thread.error_code = error_code; |
7f38551fc ptrace: x86: impl... |
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 |
memset(info, 0, sizeof(*info)); info->si_signo = SIGTRAP; info->si_code = si_code; info->si_addr = user_mode_vm(regs) ? (void __user *)regs->ip : NULL; } void user_single_step_siginfo(struct task_struct *tsk, struct pt_regs *regs, struct siginfo *info) { fill_sigtrap_info(tsk, regs, 0, TRAP_BRKPT, info); } |
1da177e4c Linux-2.6.12-rc2 |
1296 |
|
7f38551fc ptrace: x86: impl... |
1297 1298 1299 1300 |
void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code, int si_code) { struct siginfo info; |
1da177e4c Linux-2.6.12-rc2 |
1301 |
|
7f38551fc ptrace: x86: impl... |
1302 |
fill_sigtrap_info(tsk, regs, error_code, si_code, &info); |
27b46d766 spelling fixes: a... |
1303 |
/* Send us the fake SIGTRAP */ |
1da177e4c Linux-2.6.12-rc2 |
1304 1305 |
force_sig_info(SIGTRAP, &info, tsk); } |
86976cd80 x86: x86 ptrace m... |
1306 |
|
d4d671501 x86 ptrace: unify... |
1307 1308 1309 |
#ifdef CONFIG_X86_32 # define IS_IA32 1 #elif defined CONFIG_IA32_EMULATION |
ccbe495ca x86-64: syscall-a... |
1310 |
# define IS_IA32 is_compat_task() |
d4d671501 x86 ptrace: unify... |
1311 1312 1313 1314 1315 1316 1317 1318 |
#else # define IS_IA32 0 #endif /* * We must return the syscall number to actually look up in the table. * This can be -1L to skip running any syscall at all. */ |
1b4ac2a93 x86: Get rid of a... |
1319 |
long syscall_trace_enter(struct pt_regs *regs) |
86976cd80 x86: x86 ptrace m... |
1320 |
{ |
d4d671501 x86 ptrace: unify... |
1321 |
long ret = 0; |
380fdd758 x86 ptrace: user-... |
1322 1323 1324 1325 1326 1327 1328 1329 1330 |
/* * If we stepped into a sysenter/syscall insn, it trapped in * kernel mode; do_debug() cleared TF and set TIF_SINGLESTEP. * If user-mode had set TF itself, then it's still clear from * do_debug() and we need to set it again to restore the user * state. If we entered on the slow path, TF was already set. */ if (test_thread_flag(TIF_SINGLESTEP)) regs->flags |= X86_EFLAGS_TF; |
86976cd80 x86: x86 ptrace m... |
1331 1332 |
/* do the secure computing check first */ secure_computing(regs->orig_ax); |
d4d671501 x86 ptrace: unify... |
1333 1334 |
if (unlikely(test_thread_flag(TIF_SYSCALL_EMU))) ret = -1L; |
eeea3c3ff x86: tracehook sy... |
1335 1336 1337 |
if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) && tracehook_report_syscall_entry(regs)) ret = -1L; |
86976cd80 x86: x86 ptrace m... |
1338 |
|
667000011 tracing: Rename F... |
1339 |
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
1c569f026 tracing: Create g... |
1340 |
trace_sys_enter(regs, regs->orig_ax); |
1b3fa2ce6 tracing/x86: basi... |
1341 |
|
86976cd80 x86: x86 ptrace m... |
1342 |
if (unlikely(current->audit_context)) { |
d4d671501 x86 ptrace: unify... |
1343 |
if (IS_IA32) |
86976cd80 x86: x86 ptrace m... |
1344 1345 1346 1347 |
audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_ax, regs->bx, regs->cx, regs->dx, regs->si); |
d4d671501 x86 ptrace: unify... |
1348 1349 |
#ifdef CONFIG_X86_64 else |
86976cd80 x86: x86 ptrace m... |
1350 1351 1352 1353 |
audit_syscall_entry(AUDIT_ARCH_X86_64, regs->orig_ax, regs->di, regs->si, regs->dx, regs->r10); |
d4d671501 x86 ptrace: unify... |
1354 |
#endif |
86976cd80 x86: x86 ptrace m... |
1355 |
} |
d4d671501 x86 ptrace: unify... |
1356 1357 |
return ret ?: regs->orig_ax; |
86976cd80 x86: x86 ptrace m... |
1358 |
} |
1b4ac2a93 x86: Get rid of a... |
1359 |
void syscall_trace_leave(struct pt_regs *regs) |
86976cd80 x86: x86 ptrace m... |
1360 |
{ |
d51965037 ptrace: x86: chan... |
1361 |
bool step; |
86976cd80 x86: x86 ptrace m... |
1362 1363 |
if (unlikely(current->audit_context)) audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax); |
667000011 tracing: Rename F... |
1364 |
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
1c569f026 tracing: Create g... |
1365 |
trace_sys_exit(regs, regs->ax); |
1b3fa2ce6 tracing/x86: basi... |
1366 |
|
d4d671501 x86 ptrace: unify... |
1367 1368 1369 1370 |
/* * If TIF_SYSCALL_EMU is set, we only get here because of * TIF_SINGLESTEP (i.e. this is PTRACE_SYSEMU_SINGLESTEP). * We already reported this syscall instruction in |
d51965037 ptrace: x86: chan... |
1371 |
* syscall_trace_enter(). |
d4d671501 x86 ptrace: unify... |
1372 |
*/ |
d51965037 ptrace: x86: chan... |
1373 1374 1375 1376 |
step = unlikely(test_thread_flag(TIF_SINGLESTEP)) && !test_thread_flag(TIF_SYSCALL_EMU); if (step || test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall_exit(regs, step); |
d4d671501 x86 ptrace: unify... |
1377 |
} |