git: 4c3a868d13c0 - main - fcntl(F_SETFL): only allow one thread to perform F_SETFL
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 20 Sep 2025 19:34:28 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=4c3a868d13c053ef173268cdfe1365978a282178
commit 4c3a868d13c053ef173268cdfe1365978a282178
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2025-09-11 10:05:04 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2025-09-20 19:24:10 +0000
fcntl(F_SETFL): only allow one thread to perform F_SETFL
Use f_vflags file locking for this.
Allowing more than one thread handling F_SETFL might cause de-sync
between real driver state and flags.
Reviewed by: markj
Tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Differential revision: https://reviews.freebsd.org/D52487
---
sys/kern/kern_descrip.c | 3 +++
sys/kern/vfs_vnops.c | 36 ++++++++++++++++++++++++++++++++++++
sys/sys/file.h | 4 ++++
3 files changed, 43 insertions(+)
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 2a833d2eafbe..19118eb7f275 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -658,6 +658,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
error = EBADF;
break;
}
+ fsetfl_lock(fp);
do {
tmp = flg = fp->f_flag;
tmp &= ~FCNTLFLAGS;
@@ -677,6 +678,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
if (error != 0)
goto revert_nonblock;
}
+ fsetfl_unlock(fp);
fdrop(fp, td);
break;
revert_nonblock:
@@ -691,6 +693,7 @@ revert_flags:
tmp |= got_cleared;
tmp &= ~got_set;
} while (atomic_cmpset_int(&fp->f_flag, flg, tmp) == 0);
+ fsetfl_unlock(fp);
fdrop(fp, td);
break;
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index f81c2033d95e..3d4567b6ab1e 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -896,6 +896,18 @@ foffset_read(struct file *fp)
return (atomic_load_long(&fp->f_offset));
}
+void
+fsetfl_lock(struct file *fp)
+{
+ file_v_lock(fp, FILE_V_SETFL_LOCKED, FILE_V_SETFL_LOCK_WAITING);
+}
+
+void
+fsetfl_unlock(struct file *fp)
+{
+ file_v_unlock(fp, FILE_V_SETFL_LOCKED, FILE_V_SETFL_LOCK_WAITING);
+}
+
#else /* OFF_MAX <= LONG_MAX */
static void
@@ -971,6 +983,30 @@ foffset_read(struct file *fp)
return (foffset_lock(fp, FOF_NOLOCK));
}
+
+void
+fsetfl_lock(struct file *fp)
+{
+ struct mtx *mtxp;
+
+ mtxp = mtx_pool_find(mtxpool_sleep, fp);
+ mtx_lock(mtxp);
+ file_v_lock_mtxp(fp, mtxp, FILE_V_SETFL_LOCKED,
+ FILE_V_SETFL_LOCK_WAITING);
+ mtx_unlock(mtxp);
+}
+
+void
+fsetfl_unlock(struct file *fp)
+{
+ struct mtx *mtxp;
+
+ mtxp = mtx_pool_find(mtxpool_sleep, fp);
+ mtx_lock(mtxp);
+ file_v_unlock_mtxp(fp, mtxp, FILE_V_SETFL_LOCKED,
+ FILE_V_SETFL_LOCK_WAITING);
+ mtx_unlock(mtxp);
+}
#endif
void
diff --git a/sys/sys/file.h b/sys/sys/file.h
index 9a072121e25f..c44fd0f28929 100644
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -93,6 +93,8 @@ void foffset_lock_pair(struct file *fp1, off_t *off1p, struct file *fp2,
void foffset_lock_uio(struct file *fp, struct uio *uio, int flags);
void foffset_unlock(struct file *fp, off_t val, int flags);
void foffset_unlock_uio(struct file *fp, struct uio *uio, int flags);
+void fsetfl_lock(struct file *fp);
+void fsetfl_unlock(struct file *fp);
static inline off_t
foffset_get(struct file *fp)
@@ -222,6 +224,8 @@ struct file {
#define FILE_V_FOFFSET_LOCKED 0x0001
#define FILE_V_FOFFSET_LOCK_WAITING 0x0002
+#define FILE_V_SETFL_LOCKED 0x0004
+#define FILE_V_SETFL_LOCK_WAITING 0x0008
#endif /* __BSD_VISIBLE */
#endif /* _KERNEL || _WANT_FILE */