Received: by 2002:a05:6358:700f:b0:131:369:b2a3 with SMTP id 15csp2824806rwo; Thu, 3 Aug 2023 16:02:14 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFXqiQZQL9fE6j605uhjCA+v3JRICIaxVCwmgUAeFGxENjipT9GV44rCvlGeDu/tu5ZtfAa X-Received: by 2002:a2e:a0cc:0:b0:2b6:df6b:84c0 with SMTP id f12-20020a2ea0cc000000b002b6df6b84c0mr139528ljm.25.1691103734579; Thu, 03 Aug 2023 16:02:14 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1691103734; cv=none; d=google.com; s=arc-20160816; b=mkCiS4Q4hR6HY1yLxc/06LIi2tncp3hWeHnE2sxkXlAJgrTP0LbRClrd3tuiHCzjCN GtPKzWkak2HeH/1AUbPbbmRZ1baPnPFxZYUjb4E6W6/izHCvx/dIQLVS1D5779qhfvSv 12dhVYkPm8CHfW+rqasKDgbGcAP+hdB5tQSfVQ+zPvmZJSXH5xTT8VQ/NgcLkmnd5hTx aKny2SMPzWNPiy8NE81+efBOaO0yHWUmNy1bCixxAqTaQMKofGSQcY8LKYuRrMD9zKRq TErWVgzNkZt1GaLbdGqu6RR1lvsa9LTGQYtJrCEWoL2LVNr+4Rm+XqqnI24ahZ8V64DD v+iQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:to:content-transfer-encoding:mime-version :message-id:date:subject:cc:from:dkim-signature; bh=kAlJWo27YckxGe+Mw6BTj+vxeILwstk0mYPg6rjf2Hs=; fh=faVQQ2oivvefZvEFnd3x9CGlzVMlW8gM0cnn83pyfa8=; b=vJJMuhPjM75SEsZ92QQoef/XgKU3cXrZTtSet4vBJeZi37jJBpxK1abMFDLahSWvFM 9lHU5mRC/j+OrezcdrtDEk1BKpKZHztuaWI3l5QM9WoqzVlWK96FPz+2p149Oe0EeTCn N2kXN6i2M0BxA6MuMhhqjbFk5F1I72095l+u9VnrVLT4oxUwdMkdJlYCtTl2IW1xULPj xLXjMnYMdt+/R2fKz1TB9UE/WF0Q5hhJIBadGXkG50VUCrGK4GKcA2Jj6Lhque9n2lwO hWT3o0PvbYQR1gwTcmEc8a+zheH1ZMtE6rYJghm45oRGsD8XDUz8xsqRyKZnm+4G/sha YCxg== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20221208 header.b=AcL5dzc9; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id u7-20020a170906b10700b00992ca771e3asi577239ejy.48.2023.08.03.16.01.49; Thu, 03 Aug 2023 16:02:14 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20221208 header.b=AcL5dzc9; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232421AbjHCWqC (ORCPT + 99 others); Thu, 3 Aug 2023 18:46:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39690 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232328AbjHCWpD (ORCPT ); Thu, 3 Aug 2023 18:45:03 -0400 Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 91CC130C0 for ; Thu, 3 Aug 2023 15:45:01 -0700 (PDT) Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-563c7aabf38so1815353a12.0 for ; Thu, 03 Aug 2023 15:45:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1691102701; x=1691707501; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=kAlJWo27YckxGe+Mw6BTj+vxeILwstk0mYPg6rjf2Hs=; b=AcL5dzc9c7sV9ducujGWgHv687kRJ/vhcG6lFXz39juZ9x8ATzr7B3hrI7JD06d2Vy LYSxUclHk270RcAQTa+4snrIYlBakPDVZRA2DzoUiWoQbMn7VJyjFdEjpCAFP1IEm92p a4pRoVUmHQfXs+ApMjRDoPjNHbwBE01IcZjw5WrtQAb0J+ekMVlQLaYKvUsXP2UoVBIF 0o44PuXlpK/k2IuTMteshRTJXpzU9uNVYm4VksT2YKX2dOtqGzGTzDNnWgoIDTxClRMB 0vwF7BOkkju/MoeMaou+ufgxKNDvsKSZhx5IlxbZSKuNWOrXq6hEeQzqKB+hfY8M49MX Q07w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1691102701; x=1691707501; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=kAlJWo27YckxGe+Mw6BTj+vxeILwstk0mYPg6rjf2Hs=; b=N92kGo74/1YZ6TTnVbU/52yZSmDNN4ixGRnCG+1gwsyAbrnPYoN202NUgQwmUxHnTO amEfo5U6rUhK1bPZDPgi4fUuWrVhsqj1x36WnQwZfpeojiFQ3zrMdhR439lyaRwA6rOR gj49+2qslGomSC8AOduuEbU/ZWwtcuQelRRl7NowlrW0OTIiabGV0aluRAGEVdF2oFFh urjXa1zeZxPgqWtg2n59NoEopSlPwIvwpDxHFPP7xkyXciAtPlqspoRnghSnTKdfWsAa tWKIt+yuIsgyInt7SltrmTnQ7jpRIfUpVnoi8pJLE/ARALyjSX/WgqTWf2tF2ZdI0GI5 Nq6g== X-Gm-Message-State: AOJu0YyAMaZP0eacdPM1SF5bPrBAZDkOS2M5NrpsHTYyuLvisgfGBMri oCSP2npYZ83k/z6T0fvlFtM= X-Received: by 2002:a17:90b:1e10:b0:268:94b:8d0 with SMTP id pg16-20020a17090b1e1000b00268094b08d0mr126790pjb.11.1691102700837; Thu, 03 Aug 2023 15:45:00 -0700 (PDT) Received: from haorong.ba.rivosinc.com ([66.220.2.162]) by smtp.gmail.com with ESMTPSA id e16-20020a17090301d000b001bc18e579aesm359196plh.101.2023.08.03.15.44.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 03 Aug 2023 15:45:00 -0700 (PDT) From: Haorong Lu Cc: Haorong Lu , Paul Walmsley , Palmer Dabbelt , Albert Ou , Conor Dooley , Andy Chiu , Heiko Stuebner , Guo Ren , Al Viro , Mathis Salmen , Andrew Bresticker , Greentime Hu , Vincent Chen , linux-riscv@lists.infradead.org (open list:RISC-V ARCHITECTURE), linux-kernel@vger.kernel.org (open list) Subject: [PATCH] riscv: signal: handle syscall restart before get_signal Date: Thu, 3 Aug 2023 15:44:54 -0700 Message-ID: <20230803224458.4156006-1-ancientmodern4@gmail.com> X-Mailer: git-send-email 2.41.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM,RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In the current riscv implementation, blocking syscalls like read() may not correctly restart after being interrupted by ptrace. This problem arises when the syscall restart process in arch_do_signal_or_restart() is bypassed due to changes to the regs->cause register, such as an ebreak instruction. Steps to reproduce: 1. Interrupt the tracee process with PTRACE_SEIZE & PTRACE_INTERRUPT. 2. Backup original registers and instruction at new_pc. 3. Change pc to new_pc, and inject an instruction (like ebreak) to this address. 4. Resume with PTRACE_CONT and wait for the process to stop again after executing ebreak. 5. Restore original registers and instructions, and detach from the tracee process. 6. Now the read() syscall in tracee will return -1 with errno set to ERESTARTSYS. Specifically, during an interrupt, the regs->cause changes from EXC_SYSCALL to EXC_BREAKPOINT due to the injected ebreak, which is inaccessible via ptrace so we cannot restore it. This alteration breaks the syscall restart condition and ends the read() syscall with an ERESTARTSYS error. According to include/linux/errno.h, it should never be seen by user programs. X86 can avoid this issue as it checks the syscall condition using a register (orig_ax) exposed to user space. Arm64 handles syscall restart before calling get_signal, where it could be paused and inspected by ptrace/debugger. This patch adjusts the riscv implementation to arm64 style, which also checks syscall using a kernel register (syscallno). It ensures the syscall restart process is not bypassed when changes to the cause register occur, providing more consistent behavior across various architectures. For a simplified reproduction program, feel free to visit: https://github.com/ancientmodern/riscv-ptrace-bug-demo. Signed-off-by: Haorong Lu --- arch/riscv/kernel/signal.c | 85 +++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 180d951d3624..d2d7169048ea 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -391,30 +391,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 */ @@ -428,35 +404,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->a0 = regs->orig_a0; + 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. -- 2.41.0