diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 88b26af..7407c2c 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -131,6 +131,72 @@ static int fill_socket_info(struct socket *so, struct kinfo_file *kif); static int fill_vnode_info(struct vnode *vp, struct kinfo_file *kif); static int getmaxfd(struct proc *p); + +#include + +static inline bool +seq_in_modify(seq_t seqp) +{ + + return (seqp & 1); +} + +static inline void +seq_write_begin(seq_t *seqp) +{ + + MPASS(!seq_in_modify(*seqp)); + (*seqp)++; + wmb(); +} + +static inline void +seq_write_end(seq_t *seqp) +{ + + wmb(); + (*seqp)++; + MPASS(!seq_in_modify(*seqp)); +} + +static __inline seq_t +seq_read(seq_t *seqp) +{ + seq_t ret; + + for (;;) { + rmb(); + ret = *seqp; + if (seq_in_modify(ret)) { + cpu_spinwait(); + continue; + } + break; + } + rmb(); + + return (ret); +} + + +static inline seq_t +seq_consistent_nomb(seq_t *seqp, seq_t oldseqp) +{ + + MPASS(!seq_in_modify(oldseqp)); + return (*seqp == oldseqp); +} + +static inline seq_t +seq_consistent(seq_t *seqp, seq_t oldseqp) +{ + + rmb(); + return (seq_consistent_nomb(seqp, oldseqp)); +} + +#define fd_seq(fdp, fd) (&(fdp)->fd_ofiles[(fd)].fde_seq) + /* * Each process has: * @@ -301,9 +367,11 @@ fdfree(struct filedesc *fdp, int fd) struct filedescent *fde; fde = &fdp->fd_ofiles[fd]; + seq_write_begin(&fde->fde_seq); filecaps_free(&fde->fde_caps); - bzero(fde, sizeof(*fde)); + bzero(fde_change(fde), fde_change_size); fdunused(fdp, fd); + seq_write_end(&fde->fde_seq); } /* @@ -878,8 +946,9 @@ do_dup(struct thread *td, int flags, int old, int new, /* * Duplicate the source descriptor. */ + seq_write_begin(&newfde->fde_seq); filecaps_free(&newfde->fde_caps); - *newfde = *oldfde; + memcpy(fde_change(newfde), fde_change(oldfde), fde_change_size); filecaps_copy(&oldfde->fde_caps, &newfde->fde_caps); if ((flags & DUP_CLOEXEC) != 0) newfde->fde_flags = oldfde->fde_flags | UF_EXCLOSE; @@ -887,6 +956,7 @@ do_dup(struct thread *td, int flags, int old, int new, newfde->fde_flags = oldfde->fde_flags & ~UF_EXCLOSE; if (new > fdp->fd_lastfile) fdp->fd_lastfile = new; + seq_write_end(&newfde->fde_seq); *retval = new; if (delfp != NULL) { @@ -1753,6 +1823,7 @@ finstall(struct thread *td, struct file *fp, int *fd, int flags, } fhold(fp); fde = &fdp->fd_ofiles[*fd]; + seq_write_begin(&fde->fde_seq); fde->fde_file = fp; if ((flags & O_CLOEXEC) != 0) fde->fde_flags |= UF_EXCLOSE; @@ -1760,6 +1831,7 @@ finstall(struct thread *td, struct file *fp, int *fd, int flags, filecaps_move(fcaps, &fde->fde_caps); else filecaps_fill(&fde->fde_caps); + seq_write_end(&fde->fde_seq); FILEDESC_XUNLOCK(fdp); return (0); } @@ -2313,6 +2385,8 @@ fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, cap_rights_t haverights; int error; #endif + struct filedescent fde; + seq_t seq; /* * Avoid reads reordering and then a first access to the @@ -2329,19 +2403,21 @@ fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, * due to preemption. */ for (;;) { - fp = fdp->fd_ofiles[fd].fde_file; + seq = seq_read(fd_seq(fdp, fd)); + fde = fdp->fd_ofiles[fd]; + fp = fde.fde_file; if (fp == NULL) return (EBADF); #ifdef CAPABILITIES - haverights = *cap_rights(fdp, fd); + haverights = fde.fde_rights; if (needrightsp != NULL) { error = cap_check(&haverights, needrightsp); - if (error != 0) - return (error); - if (cap_rights_is_set(needrightsp, CAP_FCNTL)) { + if (error == 0 && cap_rights_is_set(needrightsp, CAP_FCNTL)) error = cap_fcntl_check(fdp, fd, needfcntl); - if (error != 0) - return (error); + if (error != 0) { + if (!seq_consistent(fd_seq(fdp, fd), seq)) + continue; + return (error); } } #endif @@ -2354,7 +2430,7 @@ fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, */ if (atomic_cmpset_acq_int(&fp->f_count, count, count + 1) != 1) continue; - if (fp == fdp->fd_ofiles[fd].fde_file) + if (seq_consistent_nomb(fd_seq(fdp, fd), seq)) break; fdrop(fp, curthread); } diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index 516ef1b..84b38f2 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -42,6 +42,8 @@ #include +typedef uint32_t seq_t; + struct filecaps { cap_rights_t fc_rights; /* per-descriptor capability rights */ u_long *fc_ioctls; /* per-descriptor allowed ioctls */ @@ -50,6 +52,7 @@ struct filecaps { }; struct filedescent { + seq_t fde_seq; struct file *fde_file; /* file structure for open file */ struct filecaps fde_caps; /* per-descriptor rights */ uint8_t fde_flags; /* per-process open file flags */ @@ -58,6 +61,8 @@ struct filedescent { #define fde_fcntls fde_caps.fc_fcntls #define fde_ioctls fde_caps.fc_ioctls #define fde_nioctls fde_caps.fc_nioctls +#define fde_change(fde) ((char *)(fde) + sizeof(seq_t)) +#define fde_change_size (sizeof(struct filedescent) - sizeof(seq_t)) /* * This structure is used for the management of descriptors. It may be