⬢ DragonFlyBSD Kernel Audit
DF-4634 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/sound/clone.c
+++ b/sys/dev/sound/clone.c
@@ -415,11 +415,25 @@ snd_clone_gc(struct snd_clone *c)
 	/*
 	 * Visit each object in reverse order. If the object is still being
 	 * referenced by a valid open(), skip it. Look for expired objects
 	 * and either revoke its clone invocation status or mercilessly
 	 * throw it away.
+	 *
+	 * NOTE: An entry with SND_CLONE_INVOKE set has been handed to the
+	 * VFS by the dev_clone handler, but the open() that triggered it
+	 * may not have reached snd_clone_acquire() yet (it can be stalled
+	 * indefinitely behind SD_F_BUSY in PCM_WAIT).  Only SND_CLONE_BUSY
+	 * is a reliable "open has settled" marker.  Destroying an INVOKE
+	 * entry kfree()s the snd_clone_entry (and destroy_dev()s the cdev)
+	 * underneath the stalled opener: snd_clone_acquire()/snd_clone_ref()
+	 * then read and write freed memory, and the devfs teardown of a
+	 * node whose vnode is still held by the in-flight open leaks the
+	 * node and the cdev.  Leave INVOKE entries alone; they are
+	 * recycled through the tce path once the unit space is exhausted,
+	 * so worst-case memory stays bounded (~maxunit entries).
 	 */
 	TAILQ_FOREACH_REVERSE_MUTABLE(ce, &c->head, link_head, link, tce) {
-		if (!(ce->flags & SND_CLONE_BUSY) &&
-		    (!(ce->flags & SND_CLONE_INVOKE) ||
-		    SND_CLONE_EXPIRED(c, &now, &ce->tsp))) {
+		if (!(ce->flags & SND_CLONE_BUSY) &&
+		    !(ce->flags & SND_CLONE_INVOKE)) {
 			if (c->flags & SND_CLONE_GC_REVOKE) {
 				ce->flags &= ~SND_CLONE_INVOKE;
 				ce->pid = -1;