Unverified Commit 0f49d2e3 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!4761 [OLK-6.6] backport 6.7 kernel signal patch

Merge Pull Request from: @wangyaoyong 
 
**riscv: signal: handle syscall restart before get_signal**

issue: https://gitee.com/openeuler/kernel/issues/I9499I

Test

tests of [riscv-ptrace-bug-demo](https://github.com/ancientmodern/riscv-ptrace-bug-demo

) pass after patching this patch in bug recurring. 
test log:
```bash
First kick: Checking the victim sid to be 1876
Check: 1876, want 1876
First kick: PASS

Interrupting task 1969
Interrupt_task return value: 0, errno is: Success
        Checkpoint original regs and instruction, new_pc = 65536
        PTRACE_PEEKDATA return value: 0, errno is: Success
        Set new regs (pc, a0) and ebreak
        PTRACE_POKEDATA return value: 0, errno is: Success
        Running PTRACE_CONT in task 1969
        PTRACE_CONT return value: 0, errno is: Success
        Restore original regs and instruction
        PTRACE_POKEDATA return value: 0, errno is: Success
Resuming task 1969
Resume_task return value: 0, errno is: Success

Closing victim stdin
Waiting for victim to die
Final kick: Checking the victim sid still to be 1876
Check: 1876, want 1876
All PASS
```
Tested-by: default avatarMingzheng Xing <xingmingzheng@iscas.ac.cn>
 
Link:https://gitee.com/openeuler/kernel/pulls/4761

 

Reviewed-by: default avatarMingzheng Xing <xingmingzheng@iscas.ac.cn>
Reviewed-by: default avatarXie XiuQi <xiexiuqi@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parents d6ff53d4 00a30820
Loading
Loading
Loading
Loading
+46 −39
Original line number Diff line number Diff line
@@ -384,30 +384,6 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
	sigset_t *oldset = sigmask_to_save();
	int ret;

	/* Are we from a system call? */
	if (regs->cause == EXC_SYSCALL) {
		/* Avoid additional syscall restarting via ret_from_exception */
		regs->cause = -1UL;
		/* If so, check system call restarting.. */
		switch (regs->a0) {
		case -ERESTART_RESTARTBLOCK:
		case -ERESTARTNOHAND:
			regs->a0 = -EINTR;
			break;

		case -ERESTARTSYS:
			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
				regs->a0 = -EINTR;
				break;
			}
			fallthrough;
		case -ERESTARTNOINTR:
                        regs->a0 = regs->orig_a0;
			regs->epc -= 0x4;
			break;
		}
	}

	rseq_signal_deliver(ksig, regs);

	/* Set up the stack frame */
@@ -421,35 +397,66 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)

void arch_do_signal_or_restart(struct pt_regs *regs)
{
	unsigned long continue_addr = 0, restart_addr = 0;
	int retval = 0;
	struct ksignal ksig;
	bool syscall = (regs->cause == EXC_SYSCALL);

	if (get_signal(&ksig)) {
		/* Actually deliver the signal */
		handle_signal(&ksig, regs);
		return;
	}
	/* If we were from a system call, check for system call restarting */
	if (syscall) {
		continue_addr = regs->epc;
		restart_addr = continue_addr - 4;
		retval = regs->a0;

	/* Did we come from a system call? */
	if (regs->cause == EXC_SYSCALL) {
		/* Avoid additional syscall restarting via ret_from_exception */
		regs->cause = -1UL;

		/* Restart the system call - no handlers present */
		switch (regs->a0) {
		/*
		 * Prepare for system call restart. We do this here so that a
		 * debugger will see the already changed PC.
		 */
		switch (retval) {
		case -ERESTARTNOHAND:
		case -ERESTARTSYS:
		case -ERESTARTNOINTR:
                        regs->a0 = regs->orig_a0;
			regs->epc -= 0x4;
			break;
		case -ERESTART_RESTARTBLOCK:
			regs->a0 = regs->orig_a0;
			regs->a7 = __NR_restart_syscall;
			regs->epc -= 0x4;
			regs->epc = restart_addr;
			break;
		}
	}

	/*
	 * Get the signal to deliver. When running under ptrace, at this point
	 * the debugger may change all of our registers.
	 */
	if (get_signal(&ksig)) {
		/*
		 * Depending on the signal settings, we may need to revert the
		 * decision to restart the system call, but skip this if a
		 * debugger has chosen to restart at a different PC.
		 */
		if (regs->epc == restart_addr &&
		    (retval == -ERESTARTNOHAND ||
		     retval == -ERESTART_RESTARTBLOCK ||
		     (retval == -ERESTARTSYS &&
		      !(ksig.ka.sa.sa_flags & SA_RESTART)))) {
			regs->a0 = -EINTR;
			regs->epc = continue_addr;
		}

		/* Actually deliver the signal */
		handle_signal(&ksig, regs);
		return;
	}

	/*
	 * Handle restarting a different system call. As above, if a debugger
	 * has chosen to restart at a different PC, ignore the restart.
	 */
	if (syscall && regs->epc == restart_addr && retval == -ERESTART_RESTARTBLOCK)
		regs->a7 = __NR_restart_syscall;

	/*
	 * If there is no signal to deliver, we just put the saved
	 * sigmask back.