git: 6c049996ec29 - main - During F_SETFL, don't change file flags on error

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Mon, 10 Jul 2023 14:14:12 UTC
The branch main has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=6c049996ec29bad4a913b019a28f211ab84b0d3d

commit 6c049996ec29bad4a913b019a28f211ab84b0d3d
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2023-07-09 20:48:10 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2023-07-10 14:13:58 +0000

    During F_SETFL, don't change file flags on error
    
    Previously, even if the FIONBIO or FIOASYNC ioctl failed, the file's
    f_flags variable would still be changed.  Now, kern_fcntl will restore
    the original flags if the ioctl fails.
    
    PR:             265736
    Reported by:    Yuval Pavel Zholkover <paulzhol@gmail.com>
    MFC after:      2 weeks
    Reviewed by:    kib
    Differential Revision: https://reviews.freebsd.org/D40955
---
 sys/kern/kern_descrip.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 908c3352514b..ad1b86a0c76c 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -495,7 +495,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
 	struct vnode *vp;
 	struct mount *mp;
 	struct kinfo_file *kif;
-	int error, flg, kif_sz, seals, tmp;
+	int error, flg, kif_sz, seals, tmp, got_set, got_cleared;
 	uint64_t bsize;
 	off_t foffset;
 
@@ -573,12 +573,12 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
 			tmp &= ~FCNTLFLAGS;
 			tmp |= FFLAGS(arg & ~O_ACCMODE) & FCNTLFLAGS;
 		} while (atomic_cmpset_int(&fp->f_flag, flg, tmp) == 0);
+		got_set = tmp & ~flg;
+		got_cleared = flg & ~tmp;
 		tmp = fp->f_flag & FNONBLOCK;
 		error = fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td);
-		if (error != 0) {
-			fdrop(fp, td);
-			break;
-		}
+		if (error != 0)
+			goto revert_f_setfl;
 		tmp = fp->f_flag & FASYNC;
 		error = fo_ioctl(fp, FIOASYNC, &tmp, td->td_ucred, td);
 		if (error == 0) {
@@ -588,6 +588,13 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
 		atomic_clear_int(&fp->f_flag, FNONBLOCK);
 		tmp = 0;
 		(void)fo_ioctl(fp, FIONBIO, &tmp, td->td_ucred, td);
+revert_f_setfl:
+		do {
+			tmp = flg = fp->f_flag;
+			tmp &= ~FCNTLFLAGS;
+			tmp |= got_cleared;
+			tmp &= ~got_set;
+		} while (atomic_cmpset_int(&fp->f_flag, flg, tmp) == 0);
 		fdrop(fp, td);
 		break;