diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-07-16 16:15:53 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-16 16:15:53 -0400 |
commit | 1b82c9666a6f637ccb3a86d0fbe23d0427076815 (patch) | |
tree | 3e405cd6cb0be449671ef28c6275fd7ad6b99295 /arch/x86/kernel | |
parent | a3cf859321486f69506326146ab3e2fd15c05c3f (diff) | |
parent | 380fdd7585a4c2f41b48925eba85c0654b7b858b (diff) |
Merge branch 'x86/step' of git://git.kernel.org/pub/scm/linux/kernel/git/frob/linux-2.6-roland into x86/step
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r-- | arch/x86/kernel/entry_32.S | 23 | ||||
-rw-r--r-- | arch/x86/kernel/entry_64.S | 14 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 151 | ||||
-rw-r--r-- | arch/x86/kernel/signal_32.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/signal_64.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/step.c | 35 |
6 files changed, 101 insertions, 134 deletions
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 6bc07f0f1202..cadf73f70d33 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S | |||
@@ -332,7 +332,7 @@ sysenter_past_esp: | |||
332 | GET_THREAD_INFO(%ebp) | 332 | GET_THREAD_INFO(%ebp) |
333 | 333 | ||
334 | /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ | 334 | /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ |
335 | testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) | 335 | testw $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) |
336 | jnz syscall_trace_entry | 336 | jnz syscall_trace_entry |
337 | cmpl $(nr_syscalls), %eax | 337 | cmpl $(nr_syscalls), %eax |
338 | jae syscall_badsys | 338 | jae syscall_badsys |
@@ -370,7 +370,7 @@ ENTRY(system_call) | |||
370 | GET_THREAD_INFO(%ebp) | 370 | GET_THREAD_INFO(%ebp) |
371 | # system call tracing in operation / emulation | 371 | # system call tracing in operation / emulation |
372 | /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ | 372 | /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ |
373 | testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) | 373 | testw $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) |
374 | jnz syscall_trace_entry | 374 | jnz syscall_trace_entry |
375 | cmpl $(nr_syscalls), %eax | 375 | cmpl $(nr_syscalls), %eax |
376 | jae syscall_badsys | 376 | jae syscall_badsys |
@@ -383,10 +383,6 @@ syscall_exit: | |||
383 | # setting need_resched or sigpending | 383 | # setting need_resched or sigpending |
384 | # between sampling and the iret | 384 | # between sampling and the iret |
385 | TRACE_IRQS_OFF | 385 | TRACE_IRQS_OFF |
386 | testl $X86_EFLAGS_TF,PT_EFLAGS(%esp) # If tracing set singlestep flag on exit | ||
387 | jz no_singlestep | ||
388 | orl $_TIF_SINGLESTEP,TI_flags(%ebp) | ||
389 | no_singlestep: | ||
390 | movl TI_flags(%ebp), %ecx | 386 | movl TI_flags(%ebp), %ecx |
391 | testw $_TIF_ALLWORK_MASK, %cx # current->work | 387 | testw $_TIF_ALLWORK_MASK, %cx # current->work |
392 | jne syscall_exit_work | 388 | jne syscall_exit_work |
@@ -514,12 +510,8 @@ END(work_pending) | |||
514 | syscall_trace_entry: | 510 | syscall_trace_entry: |
515 | movl $-ENOSYS,PT_EAX(%esp) | 511 | movl $-ENOSYS,PT_EAX(%esp) |
516 | movl %esp, %eax | 512 | movl %esp, %eax |
517 | xorl %edx,%edx | 513 | call syscall_trace_enter |
518 | call do_syscall_trace | 514 | /* What it returned is what we'll actually use. */ |
519 | cmpl $0, %eax | ||
520 | jne resume_userspace # ret != 0 -> running under PTRACE_SYSEMU, | ||
521 | # so must skip actual syscall | ||
522 | movl PT_ORIG_EAX(%esp), %eax | ||
523 | cmpl $(nr_syscalls), %eax | 515 | cmpl $(nr_syscalls), %eax |
524 | jnae syscall_call | 516 | jnae syscall_call |
525 | jmp syscall_exit | 517 | jmp syscall_exit |
@@ -528,14 +520,13 @@ END(syscall_trace_entry) | |||
528 | # perform syscall exit tracing | 520 | # perform syscall exit tracing |
529 | ALIGN | 521 | ALIGN |
530 | syscall_exit_work: | 522 | syscall_exit_work: |
531 | testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP), %cl | 523 | testb $_TIF_WORK_SYSCALL_EXIT, %cl |
532 | jz work_pending | 524 | jz work_pending |
533 | TRACE_IRQS_ON | 525 | TRACE_IRQS_ON |
534 | ENABLE_INTERRUPTS(CLBR_ANY) # could let do_syscall_trace() call | 526 | ENABLE_INTERRUPTS(CLBR_ANY) # could let syscall_trace_leave() call |
535 | # schedule() instead | 527 | # schedule() instead |
536 | movl %esp, %eax | 528 | movl %esp, %eax |
537 | movl $1, %edx | 529 | call syscall_trace_leave |
538 | call do_syscall_trace | ||
539 | jmp resume_userspace | 530 | jmp resume_userspace |
540 | END(syscall_exit_work) | 531 | END(syscall_exit_work) |
541 | CFI_ENDPROC | 532 | CFI_ENDPROC |
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index ae63e584c340..63001c6ecf6d 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
@@ -349,8 +349,7 @@ ENTRY(system_call_after_swapgs) | |||
349 | movq %rcx,RIP-ARGOFFSET(%rsp) | 349 | movq %rcx,RIP-ARGOFFSET(%rsp) |
350 | CFI_REL_OFFSET rip,RIP-ARGOFFSET | 350 | CFI_REL_OFFSET rip,RIP-ARGOFFSET |
351 | GET_THREAD_INFO(%rcx) | 351 | GET_THREAD_INFO(%rcx) |
352 | testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP), \ | 352 | testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%rcx) |
353 | TI_flags(%rcx) | ||
354 | jnz tracesys | 353 | jnz tracesys |
355 | cmpq $__NR_syscall_max,%rax | 354 | cmpq $__NR_syscall_max,%rax |
356 | ja badsys | 355 | ja badsys |
@@ -430,7 +429,12 @@ tracesys: | |||
430 | FIXUP_TOP_OF_STACK %rdi | 429 | FIXUP_TOP_OF_STACK %rdi |
431 | movq %rsp,%rdi | 430 | movq %rsp,%rdi |
432 | call syscall_trace_enter | 431 | call syscall_trace_enter |
433 | LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ | 432 | /* |
433 | * Reload arg registers from stack in case ptrace changed them. | ||
434 | * We don't reload %rax because syscall_trace_enter() returned | ||
435 | * the value it wants us to use in the table lookup. | ||
436 | */ | ||
437 | LOAD_ARGS ARGOFFSET, 1 | ||
434 | RESTORE_REST | 438 | RESTORE_REST |
435 | cmpq $__NR_syscall_max,%rax | 439 | cmpq $__NR_syscall_max,%rax |
436 | ja int_ret_from_sys_call /* RAX(%rsp) set to -ENOSYS above */ | 440 | ja int_ret_from_sys_call /* RAX(%rsp) set to -ENOSYS above */ |
@@ -483,7 +487,7 @@ int_very_careful: | |||
483 | ENABLE_INTERRUPTS(CLBR_NONE) | 487 | ENABLE_INTERRUPTS(CLBR_NONE) |
484 | SAVE_REST | 488 | SAVE_REST |
485 | /* Check for syscall exit trace */ | 489 | /* Check for syscall exit trace */ |
486 | testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP),%edx | 490 | testl $_TIF_WORK_SYSCALL_EXIT,%edx |
487 | jz int_signal | 491 | jz int_signal |
488 | pushq %rdi | 492 | pushq %rdi |
489 | CFI_ADJUST_CFA_OFFSET 8 | 493 | CFI_ADJUST_CFA_OFFSET 8 |
@@ -491,7 +495,7 @@ int_very_careful: | |||
491 | call syscall_trace_leave | 495 | call syscall_trace_leave |
492 | popq %rdi | 496 | popq %rdi |
493 | CFI_ADJUST_CFA_OFFSET -8 | 497 | CFI_ADJUST_CFA_OFFSET -8 |
494 | andl $~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP),%edi | 498 | andl $~(_TIF_WORK_SYSCALL_EXIT|_TIF_SYSCALL_EMU),%edi |
495 | jmp int_restore_rest | 499 | jmp int_restore_rest |
496 | 500 | ||
497 | int_signal: | 501 | int_signal: |
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 77040b6070e1..e37dccce85db 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -1357,8 +1357,6 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |||
1357 | #endif | 1357 | #endif |
1358 | } | 1358 | } |
1359 | 1359 | ||
1360 | #ifdef CONFIG_X86_32 | ||
1361 | |||
1362 | void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) | 1360 | void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) |
1363 | { | 1361 | { |
1364 | struct siginfo info; | 1362 | struct siginfo info; |
@@ -1377,89 +1375,10 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) | |||
1377 | force_sig_info(SIGTRAP, &info, tsk); | 1375 | force_sig_info(SIGTRAP, &info, tsk); |
1378 | } | 1376 | } |
1379 | 1377 | ||
1380 | /* notification of system call entry/exit | ||
1381 | * - triggered by current->work.syscall_trace | ||
1382 | */ | ||
1383 | int do_syscall_trace(struct pt_regs *regs, int entryexit) | ||
1384 | { | ||
1385 | int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU); | ||
1386 | /* | ||
1387 | * With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP for syscall | ||
1388 | * interception | ||
1389 | */ | ||
1390 | int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP); | ||
1391 | int ret = 0; | ||
1392 | |||
1393 | /* do the secure computing check first */ | ||
1394 | if (!entryexit) | ||
1395 | secure_computing(regs->orig_ax); | ||
1396 | |||
1397 | if (unlikely(current->audit_context)) { | ||
1398 | if (entryexit) | ||
1399 | audit_syscall_exit(AUDITSC_RESULT(regs->ax), | ||
1400 | regs->ax); | ||
1401 | /* Debug traps, when using PTRACE_SINGLESTEP, must be sent only | ||
1402 | * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is | ||
1403 | * not used, entry.S will call us only on syscall exit, not | ||
1404 | * entry; so when TIF_SYSCALL_AUDIT is used we must avoid | ||
1405 | * calling send_sigtrap() on syscall entry. | ||
1406 | * | ||
1407 | * Note that when PTRACE_SYSEMU_SINGLESTEP is used, | ||
1408 | * is_singlestep is false, despite his name, so we will still do | ||
1409 | * the correct thing. | ||
1410 | */ | ||
1411 | else if (is_singlestep) | ||
1412 | goto out; | ||
1413 | } | ||
1414 | |||
1415 | if (!(current->ptrace & PT_PTRACED)) | ||
1416 | goto out; | ||
1417 | |||
1418 | /* If a process stops on the 1st tracepoint with SYSCALL_TRACE | ||
1419 | * and then is resumed with SYSEMU_SINGLESTEP, it will come in | ||
1420 | * here. We have to check this and return */ | ||
1421 | if (is_sysemu && entryexit) | ||
1422 | return 0; | ||
1423 | |||
1424 | /* Fake a debug trap */ | ||
1425 | if (is_singlestep) | ||
1426 | send_sigtrap(current, regs, 0); | ||
1427 | |||
1428 | if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_sysemu) | ||
1429 | goto out; | ||
1430 | |||
1431 | /* the 0x80 provides a way for the tracing parent to distinguish | ||
1432 | between a syscall stop and SIGTRAP delivery */ | ||
1433 | /* Note that the debugger could change the result of test_thread_flag!*/ | ||
1434 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80:0)); | ||
1435 | |||
1436 | /* | ||
1437 | * this isn't the same as continuing with a signal, but it will do | ||
1438 | * for normal use. strace only continues with a signal if the | ||
1439 | * stopping signal is not SIGTRAP. -brl | ||
1440 | */ | ||
1441 | if (current->exit_code) { | ||
1442 | send_sig(current->exit_code, current, 1); | ||
1443 | current->exit_code = 0; | ||
1444 | } | ||
1445 | ret = is_sysemu; | ||
1446 | out: | ||
1447 | if (unlikely(current->audit_context) && !entryexit) | ||
1448 | audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_ax, | ||
1449 | regs->bx, regs->cx, regs->dx, regs->si); | ||
1450 | if (ret == 0) | ||
1451 | return 0; | ||
1452 | |||
1453 | regs->orig_ax = -1; /* force skip of syscall restarting */ | ||
1454 | if (unlikely(current->audit_context)) | ||
1455 | audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax); | ||
1456 | return 1; | ||
1457 | } | ||
1458 | |||
1459 | #else /* CONFIG_X86_64 */ | ||
1460 | |||
1461 | static void syscall_trace(struct pt_regs *regs) | 1378 | static void syscall_trace(struct pt_regs *regs) |
1462 | { | 1379 | { |
1380 | if (!(current->ptrace & PT_PTRACED)) | ||
1381 | return; | ||
1463 | 1382 | ||
1464 | #if 0 | 1383 | #if 0 |
1465 | printk("trace %s ip %lx sp %lx ax %d origrax %d caller %lx tiflags %x ptrace %x\n", | 1384 | printk("trace %s ip %lx sp %lx ax %d origrax %d caller %lx tiflags %x ptrace %x\n", |
@@ -1481,39 +1400,81 @@ static void syscall_trace(struct pt_regs *regs) | |||
1481 | } | 1400 | } |
1482 | } | 1401 | } |
1483 | 1402 | ||
1484 | asmlinkage void syscall_trace_enter(struct pt_regs *regs) | 1403 | #ifdef CONFIG_X86_32 |
1404 | # define IS_IA32 1 | ||
1405 | #elif defined CONFIG_IA32_EMULATION | ||
1406 | # define IS_IA32 test_thread_flag(TIF_IA32) | ||
1407 | #else | ||
1408 | # define IS_IA32 0 | ||
1409 | #endif | ||
1410 | |||
1411 | /* | ||
1412 | * We must return the syscall number to actually look up in the table. | ||
1413 | * This can be -1L to skip running any syscall at all. | ||
1414 | */ | ||
1415 | asmregparm long syscall_trace_enter(struct pt_regs *regs) | ||
1485 | { | 1416 | { |
1417 | long ret = 0; | ||
1418 | |||
1419 | /* | ||
1420 | * If we stepped into a sysenter/syscall insn, it trapped in | ||
1421 | * kernel mode; do_debug() cleared TF and set TIF_SINGLESTEP. | ||
1422 | * If user-mode had set TF itself, then it's still clear from | ||
1423 | * do_debug() and we need to set it again to restore the user | ||
1424 | * state. If we entered on the slow path, TF was already set. | ||
1425 | */ | ||
1426 | if (test_thread_flag(TIF_SINGLESTEP)) | ||
1427 | regs->flags |= X86_EFLAGS_TF; | ||
1428 | |||
1486 | /* do the secure computing check first */ | 1429 | /* do the secure computing check first */ |
1487 | secure_computing(regs->orig_ax); | 1430 | secure_computing(regs->orig_ax); |
1488 | 1431 | ||
1489 | if (test_thread_flag(TIF_SYSCALL_TRACE) | 1432 | if (unlikely(test_thread_flag(TIF_SYSCALL_EMU))) |
1490 | && (current->ptrace & PT_PTRACED)) | 1433 | ret = -1L; |
1434 | |||
1435 | if (ret || test_thread_flag(TIF_SYSCALL_TRACE)) | ||
1491 | syscall_trace(regs); | 1436 | syscall_trace(regs); |
1492 | 1437 | ||
1493 | if (unlikely(current->audit_context)) { | 1438 | if (unlikely(current->audit_context)) { |
1494 | if (test_thread_flag(TIF_IA32)) { | 1439 | if (IS_IA32) |
1495 | audit_syscall_entry(AUDIT_ARCH_I386, | 1440 | audit_syscall_entry(AUDIT_ARCH_I386, |
1496 | regs->orig_ax, | 1441 | regs->orig_ax, |
1497 | regs->bx, regs->cx, | 1442 | regs->bx, regs->cx, |
1498 | regs->dx, regs->si); | 1443 | regs->dx, regs->si); |
1499 | } else { | 1444 | #ifdef CONFIG_X86_64 |
1445 | else | ||
1500 | audit_syscall_entry(AUDIT_ARCH_X86_64, | 1446 | audit_syscall_entry(AUDIT_ARCH_X86_64, |
1501 | regs->orig_ax, | 1447 | regs->orig_ax, |
1502 | regs->di, regs->si, | 1448 | regs->di, regs->si, |
1503 | regs->dx, regs->r10); | 1449 | regs->dx, regs->r10); |
1504 | } | 1450 | #endif |
1505 | } | 1451 | } |
1452 | |||
1453 | return ret ?: regs->orig_ax; | ||
1506 | } | 1454 | } |
1507 | 1455 | ||
1508 | asmlinkage void syscall_trace_leave(struct pt_regs *regs) | 1456 | asmregparm void syscall_trace_leave(struct pt_regs *regs) |
1509 | { | 1457 | { |
1510 | if (unlikely(current->audit_context)) | 1458 | if (unlikely(current->audit_context)) |
1511 | audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax); | 1459 | audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax); |
1512 | 1460 | ||
1513 | if ((test_thread_flag(TIF_SYSCALL_TRACE) | 1461 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
1514 | || test_thread_flag(TIF_SINGLESTEP)) | ||
1515 | && (current->ptrace & PT_PTRACED)) | ||
1516 | syscall_trace(regs); | 1462 | syscall_trace(regs); |
1517 | } | ||
1518 | 1463 | ||
1519 | #endif /* CONFIG_X86_32 */ | 1464 | /* |
1465 | * If TIF_SYSCALL_EMU is set, we only get here because of | ||
1466 | * TIF_SINGLESTEP (i.e. this is PTRACE_SYSEMU_SINGLESTEP). | ||
1467 | * We already reported this syscall instruction in | ||
1468 | * syscall_trace_enter(), so don't do any more now. | ||
1469 | */ | ||
1470 | if (unlikely(test_thread_flag(TIF_SYSCALL_EMU))) | ||
1471 | return; | ||
1472 | |||
1473 | /* | ||
1474 | * If we are single-stepping, synthesize a trap to follow the | ||
1475 | * system call instruction. | ||
1476 | */ | ||
1477 | if (test_thread_flag(TIF_SINGLESTEP) && | ||
1478 | (current->ptrace & PT_PTRACED)) | ||
1479 | send_sigtrap(current, regs, 0); | ||
1480 | } | ||
diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c index d92373630963..295b5f5c9389 100644 --- a/arch/x86/kernel/signal_32.c +++ b/arch/x86/kernel/signal_32.c | |||
@@ -657,12 +657,6 @@ static void do_signal(struct pt_regs *regs) | |||
657 | void | 657 | void |
658 | do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) | 658 | do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) |
659 | { | 659 | { |
660 | /* Pending single-step? */ | ||
661 | if (thread_info_flags & _TIF_SINGLESTEP) { | ||
662 | regs->flags |= X86_EFLAGS_TF; | ||
663 | clear_thread_flag(TIF_SINGLESTEP); | ||
664 | } | ||
665 | |||
666 | /* deal with pending signal delivery */ | 660 | /* deal with pending signal delivery */ |
667 | if (thread_info_flags & _TIF_SIGPENDING) | 661 | if (thread_info_flags & _TIF_SIGPENDING) |
668 | do_signal(regs); | 662 | do_signal(regs); |
diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c index e53b267662e7..bf87684474f1 100644 --- a/arch/x86/kernel/signal_64.c +++ b/arch/x86/kernel/signal_64.c | |||
@@ -487,12 +487,6 @@ static void do_signal(struct pt_regs *regs) | |||
487 | void do_notify_resume(struct pt_regs *regs, void *unused, | 487 | void do_notify_resume(struct pt_regs *regs, void *unused, |
488 | __u32 thread_info_flags) | 488 | __u32 thread_info_flags) |
489 | { | 489 | { |
490 | /* Pending single-step? */ | ||
491 | if (thread_info_flags & _TIF_SINGLESTEP) { | ||
492 | regs->flags |= X86_EFLAGS_TF; | ||
493 | clear_thread_flag(TIF_SINGLESTEP); | ||
494 | } | ||
495 | |||
496 | #ifdef CONFIG_X86_MCE | 490 | #ifdef CONFIG_X86_MCE |
497 | /* notify userspace of pending MCEs */ | 491 | /* notify userspace of pending MCEs */ |
498 | if (thread_info_flags & _TIF_MCE_NOTIFY) | 492 | if (thread_info_flags & _TIF_MCE_NOTIFY) |
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 92c20fee6781..e8b9863ef8c4 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c | |||
@@ -105,6 +105,20 @@ static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs) | |||
105 | static int enable_single_step(struct task_struct *child) | 105 | static int enable_single_step(struct task_struct *child) |
106 | { | 106 | { |
107 | struct pt_regs *regs = task_pt_regs(child); | 107 | struct pt_regs *regs = task_pt_regs(child); |
108 | unsigned long oflags; | ||
109 | |||
110 | /* | ||
111 | * If we stepped into a sysenter/syscall insn, it trapped in | ||
112 | * kernel mode; do_debug() cleared TF and set TIF_SINGLESTEP. | ||
113 | * If user-mode had set TF itself, then it's still clear from | ||
114 | * do_debug() and we need to set it again to restore the user | ||
115 | * state so we don't wrongly set TIF_FORCED_TF below. | ||
116 | * If enable_single_step() was used last and that is what | ||
117 | * set TIF_SINGLESTEP, then both TF and TIF_FORCED_TF are | ||
118 | * already set and our bookkeeping is fine. | ||
119 | */ | ||
120 | if (unlikely(test_tsk_thread_flag(child, TIF_SINGLESTEP))) | ||
121 | regs->flags |= X86_EFLAGS_TF; | ||
108 | 122 | ||
109 | /* | 123 | /* |
110 | * Always set TIF_SINGLESTEP - this guarantees that | 124 | * Always set TIF_SINGLESTEP - this guarantees that |
@@ -113,11 +127,7 @@ static int enable_single_step(struct task_struct *child) | |||
113 | */ | 127 | */ |
114 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | 128 | set_tsk_thread_flag(child, TIF_SINGLESTEP); |
115 | 129 | ||
116 | /* | 130 | oflags = regs->flags; |
117 | * If TF was already set, don't do anything else | ||
118 | */ | ||
119 | if (regs->flags & X86_EFLAGS_TF) | ||
120 | return 0; | ||
121 | 131 | ||
122 | /* Set TF on the kernel stack.. */ | 132 | /* Set TF on the kernel stack.. */ |
123 | regs->flags |= X86_EFLAGS_TF; | 133 | regs->flags |= X86_EFLAGS_TF; |
@@ -126,9 +136,22 @@ static int enable_single_step(struct task_struct *child) | |||
126 | * ..but if TF is changed by the instruction we will trace, | 136 | * ..but if TF is changed by the instruction we will trace, |
127 | * don't mark it as being "us" that set it, so that we | 137 | * don't mark it as being "us" that set it, so that we |
128 | * won't clear it by hand later. | 138 | * won't clear it by hand later. |
139 | * | ||
140 | * Note that if we don't actually execute the popf because | ||
141 | * of a signal arriving right now or suchlike, we will lose | ||
142 | * track of the fact that it really was "us" that set it. | ||
129 | */ | 143 | */ |
130 | if (is_setting_trap_flag(child, regs)) | 144 | if (is_setting_trap_flag(child, regs)) { |
145 | clear_tsk_thread_flag(child, TIF_FORCED_TF); | ||
131 | return 0; | 146 | return 0; |
147 | } | ||
148 | |||
149 | /* | ||
150 | * If TF was already set, check whether it was us who set it. | ||
151 | * If not, we should never attempt a block step. | ||
152 | */ | ||
153 | if (oflags & X86_EFLAGS_TF) | ||
154 | return test_tsk_thread_flag(child, TIF_FORCED_TF); | ||
132 | 155 | ||
133 | set_tsk_thread_flag(child, TIF_FORCED_TF); | 156 | set_tsk_thread_flag(child, TIF_FORCED_TF); |
134 | 157 | ||