Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 30 Sep 2025 14:01:48 +0000
From:      bugzilla-noreply@freebsd.org
To:        bugs@FreeBSD.org
Subject:   [Bug 289917] PID leak in proc_id_reapmap
Message-ID:  <bug-289917-227@https.bugs.freebsd.org/bugzilla/>

index | next in thread | raw e-mail

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=289917

            Bug ID: 289917
           Summary: PID leak in proc_id_reapmap
           Product: Base System
           Version: CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Many People
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: avg@FreeBSD.org

I have an easy way to reproduce a PID leak in proc_id_reapmap using timeout(1)
command.
It does not require any elevated privileges, that is, it can be done as a
regular user.

The leak is readily reproducible using these versions of FreeBSD: 14.3,
stable/13, 13.5.

The leak is a bit harder to reproduce on main, stable/15, upcoming 15.0,
stable/14.
What I mean by a bit harder is that the leak is not reproducible in a default
installation, but if I take a copy of timeout(1) command from an affected
version (e.g., 14.3), then I can reproduce the leak using the command.

I conclude that the kernel allows for a resource leak via procctl(2).

In the affected versions, timeout(1) has some bug(s) which trigger the resource
leak.
In newer versions, timeout(1) is much better behaved but procctl(2) can still
be "exploited".

By filling proc_id_reapmap it's possible to create a situation where no new PID
could be allocated.
An attempt to do so would get stuck in an infinite loop in fork_findpid.
Subsequent attempts would be blocked on procid lock.

To reproduce the issue I use a small script that kills itself like this:
    #!/bin/sh
    sleep 0.0001 ; kill $$

Then I run the script under timeout(1) like this:
    timeout 0.01 ./timeout-test.sh
Or in a loop for a more pronounced effect:
    while true ; do timeout 0.01 ./timeout-test.sh ; done


proc_id_reapmap can be checked using a custom gdb function like:
define bitset_count
    set $bitset = $arg0
    set $bitcount = $arg1
    set $c = 0
    set $i = 0
    while ($i < $bitcount)
        set $n = $i / 64
        set $b = $i % 64
        if (($bitset[$n] & (1ul << $b)) != 0)
            set $c = $c + 1
        end
        set $i = $i + 1
    end
    print $c
end

Example from a test on 14.3 (running the command just moments apart):
(kgdb) bitset_count proc_id_reapmap 100000
$1 = 23
(kgdb) bitset_count proc_id_reapmap 100000
$2 = 1485

According to my brief research the userland misbehavior of timeoout(1) was
fixed by the commit series to bin/timeout done on April 16 (2025) by bapt, with
most of those changes authored by Aaron LI.

-- 
You are receiving this mail because:
You are the assignee for the bug.

home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-289917-227>