Hello,

Milos Nikic, le dim. 06 sept. 2026 00:27:32 -0700, a ecrit:
> 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.

Could you tell us which destruction path that is?

E.g. _ports_complete_deallocate takes care of checking the references
against getting reacquired.

> 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.

This approach is a bit fishy as in it requires all iterators over the
bucket to check for dying ports. There is this one, but also
ports_inhibit_bucket_rpcs, and whatnot that could be added later. If we
can rather make the destruction paths (which is normally not diverse) be
more cautious, we'll avoid the issue for all iterators cases.

Thanks,
Samuel

> ---
>  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