aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2023-07-09 20:48:10 +0000
committerAlan Somers <asomers@FreeBSD.org>2023-09-06 20:45:11 +0000
commit14ca2bc402bb82fb59a9aede05f62cb5e634b710 (patch)
tree85d18e4755ce2b50e13517cd71c551dc465fa714
parentb2c207ddfc459b82c0ec5d0c52ab66a9cc169257 (diff)
downloadsrc-14ca2bc402bb82fb59a9aede05f62cb5e634b710.tar.gz
src-14ca2bc402bb82fb59a9aede05f62cb5e634b710.zip
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> Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D40955 (cherry picked from commit 6c049996ec29bad4a913b019a28f211ab84b0d3d)
-rw-r--r--sys/kern/kern_descrip.c17
1 files changed, 12 insertions, 5 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index ab08ca434dec..dbf4ab1828de 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -481,7 +481,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;
@@ -559,12 +559,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) {
@@ -574,6 +574,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;