DF-3208 / fix.diff
--- a/isofs/cd9660/cd9660_node.c 2026-09-08 13:09:29.861631092 +0000 +++ b/isofs/cd9660/cd9660_node.c 2026-09-08 13:09:29.853631196 +0000 @@ -126,6 +126,8 @@ break; } if (ip == NULL || ITOV(ip) != vp) { + /* hash state changed while we were blocked */ + vput(vp); goto loop; } lwkt_reltoken(&cd9660_ihash_token); @@ -181,6 +183,32 @@ } /* + * Discard a freshly allocated iso_node whose vget_internal() setup failed + * AFTER the node was published in the ihash (cd9660_ihashins). The error + * paths used to vx_put() the vnode with the node still hashed, handing the + * orphaned half-initialized VNON vnode (i_mnt == NULL, i_devvp == NULL) + * to any concurrent cd9660_ihashget() blocked in vget() on the same + * (dev, ino) -- it would return it as a SUCCESS. + * + * vp->v_data is cleared first so the later reclaim (refcount -> 0 via + * vx_put/vput -> cd9660_inactive/reclaim) skips the normal processing: + * the node is already off the hash and freed here. cd9660_inactive() and + * cd9660_reclaim() tolerate a NULL v_data. + */ +void +cd9660_ihashpurge(struct vnode *vp, struct iso_node *ip) +{ + + vp->v_data = NULL; + cd9660_ihashrem(ip); + if (ip->i_devvp != NULL) { + vrele(ip->i_devvp); + ip->i_devvp = NULL; + } + kfree(ip, M_ISOFSNODE); +} + +/* * Last reference to an inode, write the inode out and if necessary, * truncate and deallocate the file. * --- a/isofs/cd9660/cd9660_node.h 2026-09-08 13:09:29.861631092 +0000 +++ b/isofs/cd9660/cd9660_node.h 2026-09-08 13:09:29.857631143 +0000 @@ -121,6 +121,7 @@ struct iso_node *, struct buf *, enum ISO_FTYPE); struct vnode *cd9660_ihashget(cdev_t, ino_t); int cd9660_ihashins(struct iso_node *); +void cd9660_ihashpurge(struct vnode *, struct iso_node *); int cd9660_tstamp_conv7(u_char *, struct timespec *, enum ISO_FTYPE); int cd9660_tstamp_conv17(u_char *, struct timespec *); --- a/isofs/cd9660/cd9660_vfsops.c 2026-09-08 13:09:29.873630936 +0000 +++ b/isofs/cd9660/cd9660_vfsops.c 2026-09-08 13:09:29.857631143 +0000 @@ -773,6 +773,7 @@ lbn = lblkno(imp, ino); if (lbn >= imp->volume_space_size) { + cd9660_ihashpurge(vp, ip); vx_put(vp); kprintf("fhtovp: lbn exceed volume space %d\n", lbn); return (ESTALE); @@ -780,6 +781,7 @@ off = blkoff(imp, ino); if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) { + cd9660_ihashpurge(vp, ip); vx_put(vp); kprintf("fhtovp: crosses block boundary %d\n", off + ISO_DIRECTORY_RECORD_SIZE); @@ -790,6 +792,7 @@ lblktooff(imp, lbn), imp->logical_block_size, &bp); if (error) { + cd9660_ihashpurge(vp, ip); vx_put(vp); brelse(bp); kprintf("fhtovp: bread error %d\n",error); @@ -799,6 +802,7 @@ if (off + isonum_711(isodir->length) > imp->logical_block_size) { + cd9660_ihashpurge(vp, ip); vx_put(vp); if (bp != NULL) brelse(bp); @@ -835,6 +839,8 @@ if (bp != NULL) brelse(bp); if ((error = cd9660_devblkatoff(vp, (off_t)0, NULL, &bp)) != 0) { + /* i_devvp was vref'd; ihashpurge vrele()s it */ + cd9660_ihashpurge(vp, ip); vx_put(vp); return (error); } |