Somewhat unusual use case.
A comment is needed to explain why we are doing it.

Applied, thank you.

On Sat, May 31, 2025 at 5:53 AM Valentin Lab
<valentin.lab_busy...@kalysto.org> wrote:
>
> This patch fixes a long-standing zombie-process leak in crond that
> appears whenever crond itself is PID 1 (typical in minimal BusyBox
> containers).
>
> Reproducer
> ==========
>
>    mkdir /tmp/test-root-crontabs -p
>    echo "* * * * * sh -c 'sleep 1 &'" > "/tmp/test-root-crontabs/root"
>    sudo chown root:root -R /tmp/test-root-crontabs
>
>    ## Run busybox crond PID 1
>    docker run -d --rm --name busybox \
>           -v /tmp/test-root-crontabs/root:/var/spool/cron/crontabs/root:ro \
>           busybox:1.37.0-uclibc \
>           crond -f -d 0 > /dev/null
>
>    watch -n 1 '
>      echo "Expecting more zombies in $((60 - 10#$(date +%S)))s (Ctrl-C to 
> quit):"
>      ps -ax -o pid,ppid,stat,comm | egrep "\s+Z\s+"
>    '
>    echo "Cleaning busybox container"
>    docker stop busybox
>
>
> You should see one new "sleep" zombie each minute.
>
> If crond is *not* PID 1 (e.g. started via a wrapper shell), the leak
> does not appear because the wrapper—now PID 1—reaps the orphans.
>
>
> Root cause
> ==========
>
> crond only calls `waitpid()` for PIDs it tracks.  Background tasks
> (`sleep 5 &`) become orphans; the kernel re-parents them to PID 1.
> When crond *is* PID 1, it inherits these orphans but never waits for
> them, so they persist as zombies.
>
>
> Fix
> ===
>
> Add a tiny reaper loop:
>
>      while (waitpid(-1, NULL, WNOHANG) > 0);
>
> Placed at the end of `check_completions()`, it collects any remaining
> children.  On systems where crond is **not** PID 1 the loop returns
> `-ECHILD` immediately, so behaviour and overhead are unchanged.
>
>
> Testing
> =======
>
> * Reproduced the leak on `busybox:1.37.0-uclibc` and current git master.
> * Applied the patch, rebuilt BusyBox statically (with only crond),
>   bind-mounted the binary into a fresh container; zombie count stays at
>   0 after X min.
>
>   Suggested docker commmand for testing with compiled (static) binary in CWD:
>
>     docker run --rm --name busybox \
>         -v /tmp/test-root-crontabs/root:/var/spool/cron/crontabs/root:ro \
>         -v $PWD/busybox:/bin/crond \
>         busybox:1.37.0-uclibc \
>         crond -f -d 0
>
> * Verified crond still exits cleanly and runs scheduled jobs normally.
>
> Binary size impact (gcc 13.3.0, x86-64, `-Os`, static): +17 bytes.
>
> Thanks for your time and for maintaining BusyBox!
>
> Regards,
> Valentin Lab
> _______________________________________________
> busybox mailing list
> busybox@busybox.net
> https://lists.busybox.net/mailman/listinfo/busybox
_______________________________________________
busybox mailing list
busybox@busybox.net
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to