DF-4634 / fix.diff
--- 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; |