From 9f36effdbdc984a086d7ff25c484665807460d67 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Fri, 29 May 2015 14:09:37 -0700 Subject: [PATCH] kernel - Fix SMP race against fp seek position lock * Fix a SMP race against write-appends. Atomic ops are used on the struct file->f_flag field to interlock write-append operations. However, the fcntl() and flock() code was also modifying file->f_flag using non-atomic ops. * Fix fcntl() and flock() to use atomic ops. * Problem could lead to processes stuck forever in "fpoff". Reported-by: Sevan Janiyan --- sys/kern/kern_descrip.c | 18 ++++++++++++++---- sys/kern/vfs_syscalls.c | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 6c28197349..90d9a880be 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -291,8 +291,18 @@ kern_fcntl(int fd, int cmd, union fcntl_dat *dat, struct ucred *cred) error = fo_ioctl(fp, FIOASYNC, (caddr_t)&tmp, cred, NULL); } - if (error == 0) - fp->f_flag = nflags; + + /* + * If no error, must be atomically set. + */ + while (error == 0) { + oflags = fp->f_flag; + cpu_ccfence(); + nflags = (oflags & ~FCNTLFLAGS) | (nflags & FCNTLFLAGS); + if (atomic_cmpset_int(&fp->f_flag, oflags, nflags)) + break; + cpu_pause(); + } break; case F_GETOWN: @@ -2497,7 +2507,7 @@ sys_flock(struct flock_args *uap) lf.l_len = 0; if (uap->how & LOCK_UN) { lf.l_type = F_UNLCK; - fp->f_flag &= ~FHASLOCK; + atomic_clear_int(&fp->f_flag, FHASLOCK); /* race ok */ error = VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, 0); goto done; } @@ -2509,11 +2519,11 @@ sys_flock(struct flock_args *uap) error = EBADF; goto done; } - fp->f_flag |= FHASLOCK; if (uap->how & LOCK_NB) error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, 0); else error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_WAIT); + atomic_set_int(&fp->f_flag, FHASLOCK); /* race ok */ done: fdrop(fp); return (error); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 77d9a1d64f..b73fee6e87 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1966,7 +1966,7 @@ kern_open(struct nlookupdata *nd, int oflags, int mode, int *res) fdrop(fp); return (error); } - fp->f_flag |= FHASLOCK; + atomic_set_int(&fp->f_flag, FHASLOCK); /* race ok */ } #if 0 /* @@ -4575,7 +4575,7 @@ sys_fhopen(struct fhopen_args *uap) goto done; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); - fp->f_flag |= FHASLOCK; + atomic_set_int(&fp->f_flag, FHASLOCK); /* race ok */ } /* -- 2.41.0