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


Reply via email to