Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1f28773 authored by Roland McGrath's avatar Roland McGrath Committed by Ingo Molnar
Browse files

x86 single_step: TIF_FORCED_TF



This changes the single-step support to use a new thread_info flag
TIF_FORCED_TF instead of the PT_DTRACE flag in task_struct.ptrace.
This keeps arch implementation uses out of this non-arch field.

This changes the ptrace access to eflags to mask TF and maintain
the TIF_FORCED_TF flag directly if userland sets TF, instead of
relying on ptrace_signal_deliver.  The 64-bit and 32-bit kernels
are harmonized on this same behavior.  The ptrace_signal_deliver
approach works now, but this change makes the low-level register
access code reliable when called from different contexts than a
ptrace stop, which will be possible in the future.

The 64-bit do_debug exception handler is also changed not to clear TF
from user-mode registers.  This matches the 32-bit kernel's behavior.

Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 7122ec81
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -89,6 +89,15 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 val)
		__u64 *flags = &stack[offsetof(struct pt_regs, eflags)/8];

		val &= FLAG_MASK;
		/*
		 * 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 (val & X86_EFLAGS_TF)
			clear_tsk_thread_flag(child, TIF_FORCED_TF);
		else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
			val |= X86_EFLAGS_TF;
		*flags = val | (*flags & ~FLAG_MASK);
		break;
	}
@@ -179,9 +188,17 @@ static int getreg32(struct task_struct *child, unsigned regno, u32 *val)
	R32(eax, rax);
	R32(orig_eax, orig_rax);
	R32(eip, rip);
	R32(eflags, eflags);
	R32(esp, rsp);

	case offsetof(struct user32, regs.eflags):
		/*
		 * If the debugger set TF, hide it from the readout.
		 */
		*val = stack[offsetof(struct pt_regs, eflags)/8];
		if (test_tsk_thread_flag(child, TIF_FORCED_TF))
			*val &= ~X86_EFLAGS_TF;
		break;

	case offsetof(struct user32, u_debugreg[0]):
		*val = child->thread.debugreg0;
		break;
@@ -425,4 +442,3 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
	put_task_struct(child);
	return ret;
}
+0 −3
Original line number Diff line number Diff line
@@ -817,9 +817,6 @@ asmlinkage int sys_execve(struct pt_regs regs)
			(char __user * __user *) regs.edx,
			&regs);
	if (error == 0) {
		task_lock(current);
		current->ptrace &= ~PT_DTRACE;
		task_unlock(current);
		/* Make sure we don't return using sysenter.. */
		set_thread_flag(TIF_IRET);
	}
+0 −5
Original line number Diff line number Diff line
@@ -709,11 +709,6 @@ long sys_execve(char __user *name, char __user * __user *argv,
	if (IS_ERR(filename)) 
		return error;
	error = do_execve(filename, argv, envp, &regs); 
	if (error == 0) {
		task_lock(current);
		current->ptrace &= ~PT_DTRACE;
		task_unlock(current);
	}
	putname(filename);
	return error;
}
+17 −0
Original line number Diff line number Diff line
@@ -104,6 +104,15 @@ static int putreg(struct task_struct *child,
			break;
		case EFL:
			value &= FLAG_MASK;
			/*
			 * 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(child, TIF_FORCED_TF);
			else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
				value |= X86_EFLAGS_TF;
			value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
			break;
	}
@@ -119,6 +128,14 @@ static unsigned long getreg(struct task_struct *child,
	unsigned long retval = ~0UL;

	switch (regno >> 2) {
		case EFL:
			/*
			 * If the debugger set TF, hide it from the readout.
			 */
			retval = get_stack_long(child, EFL_OFFSET);
			if (test_tsk_thread_flag(child, TIF_FORCED_TF))
				retval &= ~X86_EFLAGS_TF;
			break;
		case GS:
			retval = child->thread.gs;
			break;
+20 −0
Original line number Diff line number Diff line
@@ -143,6 +143,15 @@ static int putreg(struct task_struct *child,
			return 0;
		case offsetof(struct user_regs_struct, eflags):
			value &= FLAG_MASK;
			/*
			 * 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(child, TIF_FORCED_TF);
			else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
				value |= X86_EFLAGS_TF;
			tmp = get_stack_long(child, EFL_OFFSET); 
			tmp &= ~FLAG_MASK; 
			value |= tmp;
@@ -189,6 +198,17 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno)
			if (child->thread.gsindex != GS_TLS_SEL)
				return 0;
			return get_desc_base(&child->thread.tls_array[GS_TLS]);
		case offsetof(struct user_regs_struct, eflags):
			/*
			 * If the debugger set TF, hide it from the readout.
			 */
			regno = regno - sizeof(struct pt_regs);
			val = get_stack_long(child, regno);
			if (test_tsk_thread_flag(child, TIF_IA32))
				val &= 0xffffffff;
			if (test_tsk_thread_flag(child, TIF_FORCED_TF))
				val &= ~X86_EFLAGS_TF;
			return val;
		default:
			regno = regno - sizeof(struct pt_regs);
			val = get_stack_long(child, regno);
Loading