Skip to content

Commit

Permalink
entry: Ensure trap after single-step on system call return
Browse files Browse the repository at this point in the history
Commit 2991552 ("entry: Drop usage of TIF flags in the generic syscall
code") introduced a bug on architectures using the generic syscall entry
code, in which processes stopped by PTRACE_SYSCALL do not trap on syscall
return after receiving a TIF_SINGLESTEP.

The reason is that the meaning of TIF_SINGLESTEP flag is overloaded to
cause the trap after a system call is executed, but since the above commit,
the syscall call handler only checks for the SYSCALL_WORK flags on the exit
work.

Split the meaning of TIF_SINGLESTEP such that it only means single-step
mode, and create a new type of SYSCALL_WORK to request a trap immediately
after a syscall in single-step mode.  In the current implementation, the
SYSCALL_WORK flag shadows the TIF_SINGLESTEP flag for simplicity.

Update x86 to flip this bit when a tracer enables single stepping.

Fixes: 2991552 ("entry: Drop usage of TIF flags in the generic syscall code")
Suggested-by: Linus Torvalds <[email protected]>
Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Tested-by: Kyle Huey <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
  • Loading branch information
krisman-at-collabora authored and KAGA-KOKO committed Feb 5, 2021
1 parent 1048ba8 commit 6342adc
Show file tree
Hide file tree
Showing 5 changed files with 13 additions and 14 deletions.
2 changes: 0 additions & 2 deletions arch/x86/include/asm/entry-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ static __always_inline void arch_check_user_regs(struct pt_regs *regs)
}
#define arch_check_user_regs arch_check_user_regs

#define ARCH_SYSCALL_EXIT_WORK (_TIF_SINGLESTEP)

static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs,
unsigned long ti_work)
{
Expand Down
10 changes: 8 additions & 2 deletions arch/x86/kernel/step.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,17 @@ static int enable_single_step(struct task_struct *child)
regs->flags |= X86_EFLAGS_TF;

/*
* Always set TIF_SINGLESTEP - this guarantees that
* we single-step system calls etc.. This will also
* Always set TIF_SINGLESTEP. This will also
* cause us to set TF when returning to user mode.
*/
set_tsk_thread_flag(child, TIF_SINGLESTEP);

/*
* Ensure that a trap is triggered once stepping out of a system
* call prior to executing any user instruction.
*/
set_task_syscall_work(child, SYSCALL_EXIT_TRAP);

oflags = regs->flags;

/* Set TF on the kernel stack.. */
Expand Down Expand Up @@ -230,6 +235,7 @@ void user_disable_single_step(struct task_struct *child)

/* Always clear TIF_SINGLESTEP... */
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
clear_task_syscall_work(child, SYSCALL_EXIT_TRAP);

/* But touch TF only if it was set by us.. */
if (test_and_clear_tsk_thread_flag(child, TIF_FORCED_TF))
Expand Down
1 change: 1 addition & 0 deletions include/linux/entry-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
SYSCALL_WORK_SYSCALL_TRACE | \
SYSCALL_WORK_SYSCALL_AUDIT | \
SYSCALL_WORK_SYSCALL_USER_DISPATCH | \
SYSCALL_WORK_SYSCALL_EXIT_TRAP | \
ARCH_SYSCALL_WORK_EXIT)

/*
Expand Down
2 changes: 2 additions & 0 deletions include/linux/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum syscall_work_bit {
SYSCALL_WORK_BIT_SYSCALL_EMU,
SYSCALL_WORK_BIT_SYSCALL_AUDIT,
SYSCALL_WORK_BIT_SYSCALL_USER_DISPATCH,
SYSCALL_WORK_BIT_SYSCALL_EXIT_TRAP,
};

#define SYSCALL_WORK_SECCOMP BIT(SYSCALL_WORK_BIT_SECCOMP)
Expand All @@ -51,6 +52,7 @@ enum syscall_work_bit {
#define SYSCALL_WORK_SYSCALL_EMU BIT(SYSCALL_WORK_BIT_SYSCALL_EMU)
#define SYSCALL_WORK_SYSCALL_AUDIT BIT(SYSCALL_WORK_BIT_SYSCALL_AUDIT)
#define SYSCALL_WORK_SYSCALL_USER_DISPATCH BIT(SYSCALL_WORK_BIT_SYSCALL_USER_DISPATCH)
#define SYSCALL_WORK_SYSCALL_EXIT_TRAP BIT(SYSCALL_WORK_BIT_SYSCALL_EXIT_TRAP)
#endif

#include <asm/thread_info.h>
Expand Down
12 changes: 2 additions & 10 deletions kernel/entry/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,26 +209,18 @@ static void exit_to_user_mode_prepare(struct pt_regs *regs)
lockdep_sys_exit();
}

#ifndef _TIF_SINGLESTEP
static inline bool report_single_step(unsigned long work)
{
return false;
}
#else
/*
* If SYSCALL_EMU is set, then the only reason to report is when
* TIF_SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall
* SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall
* instruction has been already reported in syscall_enter_from_user_mode().
*/
static inline bool report_single_step(unsigned long work)
{
if (work & SYSCALL_WORK_SYSCALL_EMU)
return false;

return !!(current_thread_info()->flags & _TIF_SINGLESTEP);
return work & SYSCALL_WORK_SYSCALL_EXIT_TRAP;
}
#endif


static void syscall_exit_work(struct pt_regs *regs, unsigned long work)
{
Expand Down

0 comments on commit 6342adc

Please sign in to comment.