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

Commit 39efd4ec authored by Martin Schwidefsky's avatar Martin Schwidefsky
Browse files

s390/ptrace: race of single stepping vs signal delivery



The current single step code is racy in regard to concurrent delivery
of signals. If a signal is delivered after a PER program check occurred
but before the TIF_PER_TRAP bit has been checked in entry[64].S the code
clears TIF_PER_TRAP and then calls do_signal. This is wrong, if the
instruction completed (or has been suppressed) a SIGTRAP should be
delivered to the debugger in any case. Only if the instruction has been
nullified the SIGTRAP may not be send.

The new logic always sets TIF_PER_TRAP if the program check indicates PER
tracing but removes it again for all program checks that are nullifying.
The effect is that for each change in the PSW address we now get a
single SIGTRAP.

Reported-by: default avatarAndreas Arnez <arnez@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent c68dba20
Loading
Loading
Loading
Loading
+3 −4
Original line number Original line Diff line number Diff line
@@ -231,12 +231,12 @@ sysc_work:
	jo	sysc_mcck_pending
	jo	sysc_mcck_pending
	tm	__TI_flags+3(%r12),_TIF_NEED_RESCHED
	tm	__TI_flags+3(%r12),_TIF_NEED_RESCHED
	jo	sysc_reschedule
	jo	sysc_reschedule
	tm	__TI_flags+3(%r12),_TIF_PER_TRAP
	jo	sysc_singlestep
	tm	__TI_flags+3(%r12),_TIF_SIGPENDING
	tm	__TI_flags+3(%r12),_TIF_SIGPENDING
	jo	sysc_sigpending
	jo	sysc_sigpending
	tm	__TI_flags+3(%r12),_TIF_NOTIFY_RESUME
	tm	__TI_flags+3(%r12),_TIF_NOTIFY_RESUME
	jo	sysc_notify_resume
	jo	sysc_notify_resume
	tm	__TI_flags+3(%r12),_TIF_PER_TRAP
	jo	sysc_singlestep
	j	sysc_return		# beware of critical section cleanup
	j	sysc_return		# beware of critical section cleanup


#
#
@@ -259,7 +259,6 @@ sysc_mcck_pending:
# _TIF_SIGPENDING is set, call do_signal
# _TIF_SIGPENDING is set, call do_signal
#
#
sysc_sigpending:
sysc_sigpending:
	ni	__TI_flags+3(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
	lr	%r2,%r11		# pass pointer to pt_regs
	lr	%r2,%r11		# pass pointer to pt_regs
	l	%r1,BASED(.Ldo_signal)
	l	%r1,BASED(.Ldo_signal)
	basr	%r14,%r1		# call do_signal
	basr	%r14,%r1		# call do_signal
@@ -286,7 +285,7 @@ sysc_notify_resume:
# _TIF_PER_TRAP is set, call do_per_trap
# _TIF_PER_TRAP is set, call do_per_trap
#
#
sysc_singlestep:
sysc_singlestep:
	ni	__TI_flags+3(%r12),255-(_TIF_SYSCALL | _TIF_PER_TRAP)
	ni	__TI_flags+3(%r12),255-_TIF_PER_TRAP
	lr	%r2,%r11		# pass pointer to pt_regs
	lr	%r2,%r11		# pass pointer to pt_regs
	l	%r1,BASED(.Ldo_per_trap)
	l	%r1,BASED(.Ldo_per_trap)
	la	%r14,BASED(sysc_return)
	la	%r14,BASED(sysc_return)
+3 −4
Original line number Original line Diff line number Diff line
@@ -262,12 +262,12 @@ sysc_work:
	jo	sysc_mcck_pending
	jo	sysc_mcck_pending
	tm	__TI_flags+7(%r12),_TIF_NEED_RESCHED
	tm	__TI_flags+7(%r12),_TIF_NEED_RESCHED
	jo	sysc_reschedule
	jo	sysc_reschedule
	tm	__TI_flags+7(%r12),_TIF_PER_TRAP
	jo	sysc_singlestep
	tm	__TI_flags+7(%r12),_TIF_SIGPENDING
	tm	__TI_flags+7(%r12),_TIF_SIGPENDING
	jo	sysc_sigpending
	jo	sysc_sigpending
	tm	__TI_flags+7(%r12),_TIF_NOTIFY_RESUME
	tm	__TI_flags+7(%r12),_TIF_NOTIFY_RESUME
	jo	sysc_notify_resume
	jo	sysc_notify_resume
	tm	__TI_flags+7(%r12),_TIF_PER_TRAP
	jo	sysc_singlestep
	j	sysc_return		# beware of critical section cleanup
	j	sysc_return		# beware of critical section cleanup


#
#
@@ -288,7 +288,6 @@ sysc_mcck_pending:
# _TIF_SIGPENDING is set, call do_signal
# _TIF_SIGPENDING is set, call do_signal
#
#
sysc_sigpending:
sysc_sigpending:
	ni	__TI_flags+7(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
	lgr	%r2,%r11		# pass pointer to pt_regs
	lgr	%r2,%r11		# pass pointer to pt_regs
	brasl	%r14,do_signal
	brasl	%r14,do_signal
	tm	__TI_flags+7(%r12),_TIF_SYSCALL
	tm	__TI_flags+7(%r12),_TIF_SYSCALL
@@ -313,7 +312,7 @@ sysc_notify_resume:
# _TIF_PER_TRAP is set, call do_per_trap
# _TIF_PER_TRAP is set, call do_per_trap
#
#
sysc_singlestep:
sysc_singlestep:
	ni	__TI_flags+7(%r12),255-(_TIF_SYSCALL | _TIF_PER_TRAP)
	ni	__TI_flags+7(%r12),255-_TIF_PER_TRAP
	lgr	%r2,%r11		# pass pointer to pt_regs
	lgr	%r2,%r11		# pass pointer to pt_regs
	larl	%r14,sysc_return
	larl	%r14,sysc_return
	jg	do_per_trap
	jg	do_per_trap
+2 −0
Original line number Original line Diff line number Diff line
@@ -461,6 +461,8 @@ void do_signal(struct pt_regs *regs)
			/* Restart system call with magic TIF bit. */
			/* Restart system call with magic TIF bit. */
			regs->gprs[2] = regs->orig_gpr2;
			regs->gprs[2] = regs->orig_gpr2;
			set_thread_flag(TIF_SYSCALL);
			set_thread_flag(TIF_SYSCALL);
			if (test_thread_flag(TIF_SINGLE_STEP))
				set_thread_flag(TIF_PER_TRAP);
			break;
			break;
		}
		}
	}
	}
+13 −6
Original line number Original line Diff line number Diff line
@@ -277,10 +277,16 @@ static inline int do_exception(struct pt_regs *regs, int access)
	unsigned int flags;
	unsigned int flags;
	int fault;
	int fault;


	tsk = current;
	/*
	 * The instruction that caused the program check has
	 * been nullified. Don't signal single step via SIGTRAP.
	 */
	clear_tsk_thread_flag(tsk, TIF_PER_TRAP);

	if (notify_page_fault(regs))
	if (notify_page_fault(regs))
		return 0;
		return 0;


	tsk = current;
	mm = tsk->mm;
	mm = tsk->mm;
	trans_exc_code = regs->int_parm_long;
	trans_exc_code = regs->int_parm_long;


@@ -376,11 +382,6 @@ static inline int do_exception(struct pt_regs *regs, int access)
			goto retry;
			goto retry;
		}
		}
	}
	}
	/*
	 * The instruction that caused the program check will
	 * be repeated. Don't signal single step via SIGTRAP.
	 */
	clear_tsk_thread_flag(tsk, TIF_PER_TRAP);
	fault = 0;
	fault = 0;
out_up:
out_up:
	up_read(&mm->mmap_sem);
	up_read(&mm->mmap_sem);
@@ -427,6 +428,12 @@ void __kprobes do_asce_exception(struct pt_regs *regs)
	struct vm_area_struct *vma;
	struct vm_area_struct *vma;
	unsigned long trans_exc_code;
	unsigned long trans_exc_code;


	/*
	 * The instruction that caused the program check has
	 * been nullified. Don't signal single step via SIGTRAP.
	 */
	clear_tsk_thread_flag(current, TIF_PER_TRAP);

	trans_exc_code = regs->int_parm_long;
	trans_exc_code = regs->int_parm_long;
	if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
	if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
		goto no_context;
		goto no_context;