diff --git a/include/sys/user.h b/include/sys/user.h index ebbe8f68e..2b25dd33c 100644 --- a/include/sys/user.h +++ b/include/sys/user.h @@ -30,8 +30,8 @@ * about the Linux task_struct. Since this is internal to our compatibility * layer, we make it an opaque type. * - * XXX: If the descriptor changes under us, we would get an incorrect - * reference. + * XXX: If the descriptor changes under us and we do not do a getf() between + * the change and using it, we would get an incorrect reference. */ struct uf_info; diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c index ab9830d18..86349c54a 100644 --- a/module/spl/spl-vnode.c +++ b/module/spl/spl-vnode.c @@ -656,6 +656,19 @@ vn_getf(int fd) fp = file_find(fd, current); if (fp) { + lfp = fget(fd); + fput(fp->f_file); + /* + * areleasef() can cause us to see a stale reference when + * userspace has reused a file descriptor before areleasef() + * has run. fput() the stale reference and replace it. We + * retain the original reference count such that the concurrent + * areleasef() will decrement its reference and terminate. + */ + if (lfp != fp->f_file) { + fp->f_file = lfp; + fp->f_vnode->v_file = lfp; + } atomic_inc(&fp->f_ref); spin_unlock(&vn_file_lock); return (fp);