From nobody Tue Apr 15 02:25:37 2025 X-Original-To: dev-commits-src-branches@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 4Zc7LB1lJ5z5sjTP; Tue, 15 Apr 2025 02:25:38 +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 4Zc7L96SM2z3M5j; Tue, 15 Apr 2025 02:25:37 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1744683937; 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=HBMrLyw9gGyHxKr4i5Ekrxyx+Ym1m6o8EvQTDEajPs8=; b=bJy/PDb+6MNOGbP/xTptgZTVPsYBjBFsbA4XBxSnr5rzcBNSwpxLYY9PCSQKffct1utHak W6axOjFGbEq10KON+XoVh610jgYsigbsqu0OV/pJ6hBnfXeiKEHq5THpEOIuu0W5Nbeawl x9ilAts44fDLlF8N6OoPjCOgaX6HiOjShI2/Evy0razY6rRUXvgcBAIepK2FOiw5y4w2VN iODQ76Se4v5uW6kU8ObM6SfkEnrQS952AVciIOxM4P9dmxDvGsW1UKwgptnCtpbR0ztif9 /BbnUCov1l82JPm+Novtl/qOmI7/9AJeaxP+Xn5dxFxcYr+gQ6YNdVIdUHsv9Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1744683938; a=rsa-sha256; cv=none; b=QS259hv/RBowXUkrAd6Zs7sJy7B2ZO7PoercL3fERIkSXcNygtJZGLHxTMK9qMpg4W3Fqd 1hgjWN6OskWy67sBV772GTpG1IrOsnxQN9Q3mcKjkiZSG+9rStC+w0q4gzoRqA35REbyl4 LZKHlWwDj+Q0uz4jtEXw7A44dJFi9yz/ddq6Tp40UjPw8zrDKIeOrxB/uL+p7QVnXGNB7z 79mjejsPDgK6hZFjuZb0JReE8C1c21QoYBkBq16lsXjT6inT6RsYM06FJgIB5fJcVSXFqb anNmbnjb3n/ayyfCKYkCqmW+ygS6a/VdJF/VTeEzMimhIenZVhT3dYwmzImwfw== 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=1744683937; 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=HBMrLyw9gGyHxKr4i5Ekrxyx+Ym1m6o8EvQTDEajPs8=; b=ECPu39r7dZtjDMurNaQGgwJtmTelJbvmJ7CaEshxgrHDwOweXWkZmaR7DuvQigjimOvfMY CyiMZyvIjiztllOHSvbRw9cobgbtWMRhpiuveZNaBww4J2GQY4MydfGVB2nIa9r8xtl5PQ X+XHefUkvaH38GX5RIrxzjH9pbT3Qj7loY5Kt75CcDRa+e7JqmEjrQef3z2l9CIZk6qRfz UHkKPQUlVVdbOpD/M68j0lJWtk5PT+ztolaBY17YSBBuMbFLHZirnOITqiVZAb6PS1Q/AB qqLBf5qKni7sqFA/2W9dwcNvx8NiA32YQ1hMrqcjgrJ1WJCA+cnGJNyIwj94uw== 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 4Zc7L9604xzNH; Tue, 15 Apr 2025 02:25:37 +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 53F2Pb7f014627; Tue, 15 Apr 2025 02:25:37 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 53F2PbKp014624; Tue, 15 Apr 2025 02:25:37 GMT (envelope-from git) Date: Tue, 15 Apr 2025 02:25:37 GMT Message-Id: <202504150225.53F2PbKp014624@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 1d2fd8c9cf0f - stable/14 - file: Fix offset handling in kern_copy_file_range() List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@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/stable/14 X-Git-Reftype: branch X-Git-Commit: 1d2fd8c9cf0fb796d8b7b7590288d3125398d445 Auto-Submitted: auto-generated The branch stable/14 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=1d2fd8c9cf0fb796d8b7b7590288d3125398d445 commit 1d2fd8c9cf0fb796d8b7b7590288d3125398d445 Author: Mark Johnston AuthorDate: 2025-03-31 01:23:30 +0000 Commit: Mark Johnston CommitDate: 2025-04-15 02:25:24 +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 (cherry picked from commit 197997a4c36d8be5807688a4f973ebe8ae807a6e) --- sys/kern/vfs_syscalls.c | 74 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 17ab419fb0ae..9f8c960d054c 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -4902,16 +4902,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; @@ -4953,13 +4954,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; @@ -4978,37 +5001,44 @@ kern_copy_file_range(struct thread *td, int infd, off_t *inoffp, int outfd, * If infp and outfp refer to the same file, the byte ranges cannot * overlap. */ - if (invp == outvp && ((savinoff <= savoutoff && savinoff + len > - savoutoff) || (savinoff > savoutoff && savoutoff + len > - savinoff))) { + if (invp == outvp && ((inoff <= outoff && inoff + len > + outoff) || (inoff > outoff && outoff + len > inoff))) { error = EINVAL; goto out; } /* 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);