When heavily stressed (e.g., rapid file deletions during a tar extraction),
a race condition occurs in _ports_bucket_class_iterate.
A destruction thread may drop a port's final reference (refcount hits 0)
but get blocked waiting for the _ports_htable_lock write-lock to remove
it from the ihash table. If the periodic sync thread simultaneously holds
the read-lock and iterates over the bucket, it encounters this "dead" port.
Attempting to call refcounts_ref() on it triggers a use-after-free assertion
panic in the kernel.
Since the read-lock guarantees the memory is still safe, we can simply
check if the hard refcount is 0 and skip the port, allowing the teardown
thread to finish its job once the lock is released.
---
libports/bucket-iterate.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/libports/bucket-iterate.c b/libports/bucket-iterate.c
index 9103c2977..7ac18a91d 100644
--- a/libports/bucket-iterate.c
+++ b/libports/bucket-iterate.c
@@ -58,6 +58,13 @@ _ports_bucket_class_iterate (struct hurd_ihash *ht,
if (class == 0 || pi->class == class)
{
+ /* Check if the port is already dying.
+ Because we hold the read lock, the memory is safe,
+ but if the hard refcount is 0, we must skip it to
+ prevent a use-after-free panic! */
+ if (refcounts_hard_references (&pi->refcounts) == 0)
+ continue;
+
refcounts_ref (&pi->refcounts, NULL);
p[n] = pi;
n++;
--
2.55.0