devfs - Fix use-after-free case when making pty's invisible
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 13 Sep 2010 15:20:16 +0000 (08:20 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 13 Sep 2010 15:20:16 +0000 (08:20 -0700)
* Fix a use-after-free case when making a pty devfs node invisible.
  The dev_dclose().  Move the test/flag to before the dev_dclose() call.

* Document that the pty code may destroy the device in the dev_dclose()
  call, causing the node to become stale.

Reported-by: Francois Tigeot <ftigeot@wolfpond.org>
Reminded-by: sjg
sys/vfs/devfs/devfs_vnops.c

index a213dda..7bc79ee 100644 (file)
@@ -1008,7 +1008,7 @@ devfs_spec_open(struct vop_open_args *ap)
 static int
 devfs_spec_close(struct vop_close_args *ap)
 {
-       struct devfs_node *node = DEVFS_NODE(ap->a_vp);
+       struct devfs_node *node;
        struct proc *p = curproc;
        struct vnode *vp = ap->a_vp;
        cdev_t dev = vp->v_rdev;
@@ -1054,6 +1054,14 @@ devfs_spec_close(struct vop_close_args *ap)
        if (dev && ((vp->v_flag & VRECLAIMED) ||
            (dev_dflags(dev) & D_TRACKCLOSE) ||
            (vp->v_opencount == 1))) {
+               /*
+                * Ugly pty magic, to make pty devices disappear again once
+                * they are closed.
+                */
+               node = DEVFS_NODE(ap->a_vp);
+               if (node && (node->flags & DEVFS_PTY))
+                       node->flags |= DEVFS_INVISIBLE;
+
                /*
                 * Unlock around dev_dclose()
                 */
@@ -1062,14 +1070,13 @@ devfs_spec_close(struct vop_close_args *ap)
                        needrelock = 1;
                        vn_unlock(vp);
                }
-               error = dev_dclose(dev, ap->a_fflag, S_IFCHR);
 
                /*
-                * Ugly pty magic, to make pty devices disappear again once
-                * they are closed
+                * WARNING!  If the device destroys itself the devfs node
+                *           can disappear here.
                 */
-               if (node && (node->flags & DEVFS_PTY) == DEVFS_PTY)
-                       node->flags |= DEVFS_INVISIBLE;
+               error = dev_dclose(dev, ap->a_fflag, S_IFCHR);
+               /* node is now stale */
 
                if (needrelock)
                        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);