From nobody Mon Mar 31 09:06:38 2025 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ZR4xr08Nxz5rhkK; Mon, 31 Mar 2025 09:06:40 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ZR4xq0Vfvz3YC4; Mon, 31 Mar 2025 09:06:39 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1743411999; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=8N/QzN+32GUl9BfXWU74DCnerCE+vpIZQ2dy/ElKgjw=; b=nwg6FcO1XzG6T1WWrnppvVAiCZDnY50a0gTxsbXKN4eXZOEvHOvk9bBxVi/DkMN9i72IjF RDsmf4x42DqxRrfPPLRYVRwzuidoYhMBd58vx0tbJbwMUDupRNfvSWOrfTbCNsFxZ+HX3x zDEfWBESvUhLavb9/SFh6rHVNZpo21NI7l8sxlSWDkmz2dhF/5m8ksV8+FLbILr9rwYXvW VCTUrdKIpMDJ2HqIQ/9TusCbyo9D6YX2LUPNAlJICHuWXOcGtjMXfcAMlLEhxEQB2gus2h aaDsnlHKVcGHW/as00TSKXOXij6lRDmVK9ecjHs1g5EiL38b7KIILXuvYDju/w== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1743411999; a=rsa-sha256; cv=none; b=t16A0nAIJvzYfu7tG23FOqzt9okUHa6mwFdk/qV0klh/W4w9GXZcXdbKttjQCYESr3njg4 WG9wj6RO1yrEFgI3uGb3RY9O3Kk7VLTZOaMjjEIUxHYIlClheiURBudXvnjX0Aqbd5iVGe woojXXSbfST6LmxPhFa8ikc+42+c9ICr24CEd4gvOTWgba+9g8Xz531amqnkoQCRtZZiPv DiWb35t/BXHGCCJQpNY4Jy3FBggjEAIquXLiy6/z518A7rf5znWZ6pWWvO7vx3OmUz5oyo IoPWDppXs1DcsFQDdL3J/d2zTQG2EFbWHhoaA/MtXToiLSNHVCxOD66TU7atiQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1743411999; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=8N/QzN+32GUl9BfXWU74DCnerCE+vpIZQ2dy/ElKgjw=; b=tOusGr/h6dmbkK81EorjYiBBILIposmJkG5K/glECZN/NDjobaZ8mmkAmw++sAtPwZu7ae fuqJ2L/AaB6KzP7ZUbP2kVFaMTB5YifFeiSZzrqql5JfUH7VEp3nPQv9HO17j95vgZfiPg +WyJdMSVHqvuEad190PZ+U7zvq4p66haL8iomVESINszJK4ypuudukKna6kTEgm4xCTXE+ jL9fvvr+BmA2fBYa1Mv/bNb/cpeFLe9kiYmDmWYjqvrvcNL0LRn4iPPAd4bXQvgI3h1Wui WCqdU1j+jnBXCZaiN4RXBwJ5+303ci7nOD0gY8ZfveU8D9LezIppFKp5+9lK7w== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4ZR4xp5hljzfPq; Mon, 31 Mar 2025 09:06:38 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 52V96cjg078949; Mon, 31 Mar 2025 09:06:38 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 52V96cJP078946; Mon, 31 Mar 2025 09:06:38 GMT (envelope-from git) Date: Mon, 31 Mar 2025 09:06:38 GMT Message-Id: <202503310906.52V96cJP078946@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Mark Johnston Subject: git: 197997a4c36d - main - file: Fix offset handling in kern_copy_file_range() List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 197997a4c36d8be5807688a4f973ebe8ae807a6e Auto-Submitted: auto-generated The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=197997a4c36d8be5807688a4f973ebe8ae807a6e commit 197997a4c36d8be5807688a4f973ebe8ae807a6e Author: Mark Johnston AuthorDate: 2025-03-31 01:23:30 +0000 Commit: Mark Johnston CommitDate: 2025-03-31 09:01:09 +0000 file: Fix offset handling in kern_copy_file_range() One can ask copy_file_range(2) to use the file offsets of the file descriptions that it copies from and to. We were updating those offsets without any locking, which is incorrect and can lead to unkillable loops in the event of a race (e.g., the check for overlapping ranges in kern_copy_file_range() is subject to a TOCTOU race with the following loop which range-locks the input and output file). Use foffset_lock() to serialize updates to the file descriptions, as we do for other, similar system calls. Reported by: syzkaller Reviewed by: rmacklem, kib MFC after: 2 weeks Fixes: bbbbeca3e9a3 ("Add kernel support for a Linux compatible copy_file_range(2) syscall.") Differential Revision: https://reviews.freebsd.org/D49440 --- sys/kern/vfs_syscalls.c | 73 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 41609d7c5351..a6c216d61d8c 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -4940,16 +4940,17 @@ int kern_copy_file_range(struct thread *td, int infd, off_t *inoffp, int outfd, off_t *outoffp, size_t len, unsigned int flags) { - struct file *infp, *outfp; + struct file *infp, *infp1, *outfp, *outfp1; struct vnode *invp, *outvp; int error; size_t retlen; void *rl_rcookie, *rl_wcookie; - off_t savinoff, savoutoff; + off_t inoff, outoff, savinoff, savoutoff; + bool foffsets_locked; infp = outfp = NULL; rl_rcookie = rl_wcookie = NULL; - savinoff = -1; + foffsets_locked = false; error = 0; retlen = 0; @@ -4991,13 +4992,35 @@ kern_copy_file_range(struct thread *td, int infd, off_t *inoffp, int outfd, goto out; } - /* Set the offset pointers to the correct place. */ - if (inoffp == NULL) - inoffp = &infp->f_offset; - if (outoffp == NULL) - outoffp = &outfp->f_offset; - savinoff = *inoffp; - savoutoff = *outoffp; + /* + * Figure out which file offsets we're reading from and writing to. + * If the offsets come from the file descriptions, we need to lock them, + * and locking both offsets requires a loop to avoid deadlocks. + */ + infp1 = outfp1 = NULL; + if (inoffp != NULL) + inoff = *inoffp; + else + infp1 = infp; + if (outoffp != NULL) + outoff = *outoffp; + else + outfp1 = outfp; + if (infp1 != NULL || outfp1 != NULL) { + if (infp1 == outfp1) { + /* + * Overlapping ranges are not allowed. A more thorough + * check appears below, but we must not lock the same + * offset twice. + */ + error = EINVAL; + goto out; + } + foffset_lock_pair(infp1, &inoff, outfp1, &outoff, 0); + foffsets_locked = true; + } + savinoff = inoff; + savoutoff = outoff; invp = infp->f_vnode; outvp = outfp->f_vnode; @@ -5017,8 +5040,8 @@ kern_copy_file_range(struct thread *td, int infd, off_t *inoffp, int outfd, * overlap. */ if (invp == outvp) { - if ((savinoff <= savoutoff && savinoff + len > savoutoff) || - (savinoff > savoutoff && savoutoff + len > savinoff)) { + if ((inoff <= outoff && inoff + len > outoff) || + (inoff > outoff && outoff + len > inoff)) { error = EINVAL; goto out; } @@ -5027,28 +5050,36 @@ kern_copy_file_range(struct thread *td, int infd, off_t *inoffp, int outfd, /* Range lock the byte ranges for both invp and outvp. */ for (;;) { - rl_wcookie = vn_rangelock_wlock(outvp, *outoffp, *outoffp + - len); - rl_rcookie = vn_rangelock_tryrlock(invp, *inoffp, *inoffp + - len); + rl_wcookie = vn_rangelock_wlock(outvp, outoff, outoff + len); + rl_rcookie = vn_rangelock_tryrlock(invp, inoff, inoff + len); if (rl_rcookie != NULL) break; vn_rangelock_unlock(outvp, rl_wcookie); - rl_rcookie = vn_rangelock_rlock(invp, *inoffp, *inoffp + len); + rl_rcookie = vn_rangelock_rlock(invp, inoff, inoff + len); vn_rangelock_unlock(invp, rl_rcookie); } retlen = len; - error = vn_copy_file_range(invp, inoffp, outvp, outoffp, &retlen, + error = vn_copy_file_range(invp, &inoff, outvp, &outoff, &retlen, flags, infp->f_cred, outfp->f_cred, td); out: if (rl_rcookie != NULL) vn_rangelock_unlock(invp, rl_rcookie); if (rl_wcookie != NULL) vn_rangelock_unlock(outvp, rl_wcookie); - if (savinoff != -1 && (error == EINTR || error == ERESTART)) { - *inoffp = savinoff; - *outoffp = savoutoff; + if (foffsets_locked) { + if (error == EINTR || error == ERESTART) { + inoff = savinoff; + outoff = savoutoff; + } + if (inoffp == NULL) + foffset_unlock(infp, inoff, 0); + else + *inoffp = inoff; + if (outoffp == NULL) + foffset_unlock(outfp, outoff, 0); + else + *outoffp = outoff; } if (outfp != NULL) fdrop(outfp, td);