git: 82d8c609cfb7 - main - posixshm: Fix range locking in shm_write()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 07 Apr 2025 12:01:21 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=82d8c609cfb7c6d8a9da8e30efa54240f293359e

commit 82d8c609cfb7c6d8a9da8e30efa54240f293359e
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-04-06 22:51:53 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-04-07 11:59:57 +0000

    posixshm: Fix range locking in shm_write()
    
    There is a somewhat strange case where when writing to a POSIX shm
    object, the object is not allowed to grow, and the I/O offset+length
    overflows.  In that case we simply truncate the I/O to the object size.
    Later we write-lock the range [offset, objsize).  However, we were not
    checking whether offset > objsize, in which case we're writing zero
    bytes but locking an invalid range.
    
    Modify the range locking in shm_write() to take this possibility into
    account.  While here, rename a variable to make its purpose a bit more
    clear, and add an assertion against negative offsets (which is supposed
    to be enforced by the caller of fo_write for I/O to files that aren't
    character devices).
    
    Reported by:    syzkaller
    Reviewed by:    kevans, kib
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D49673
---
 sys/kern/uipc_shm.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c
index b4016e9dd6bf..7004e4f1c6ea 100644
--- a/sys/kern/uipc_shm.c
+++ b/sys/kern/uipc_shm.c
@@ -481,7 +481,10 @@ shm_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
 	struct shmfd *shmfd;
 	void *rl_cookie;
 	int error;
-	off_t size;
+	off_t newsize;
+
+	KASSERT((flags & FOF_OFFSET) == 0 || uio->uio_offset >= 0,
+	    ("%s: negative offset", __func__));
 
 	shmfd = fp->f_data;
 #ifdef MAC
@@ -503,21 +506,23 @@ shm_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
 			return (EFBIG);
 		}
 
-		size = shmfd->shm_size;
+		newsize = atomic_load_64(&shmfd->shm_size);
 	} else {
-		size = uio->uio_offset + uio->uio_resid;
+		newsize = uio->uio_offset + uio->uio_resid;
 	}
 	if ((flags & FOF_OFFSET) == 0)
 		rl_cookie = shm_rangelock_wlock(shmfd, 0, OFF_MAX);
 	else
-		rl_cookie = shm_rangelock_wlock(shmfd, uio->uio_offset, size);
+		rl_cookie = shm_rangelock_wlock(shmfd, uio->uio_offset,
+		    MAX(newsize, uio->uio_offset));
 	if ((shmfd->shm_seals & F_SEAL_WRITE) != 0) {
 		error = EPERM;
 	} else {
 		error = 0;
 		if ((shmfd->shm_flags & SHM_GROW_ON_WRITE) != 0 &&
-		    size > shmfd->shm_size) {
-			error = shm_dotruncate_cookie(shmfd, size, rl_cookie);
+		    newsize > shmfd->shm_size) {
+			error = shm_dotruncate_cookie(shmfd, newsize,
+			    rl_cookie);
 		}
 		if (error == 0)
 			error = uiomove_object(shmfd->shm_object,