Skip to content

mkdir: exits 0 despite EEXIST from the kernel under concurrent invocation (breaks mkdir-as-mutex) #13970

Closed

Description

@ginere

Summary

Under concurrent invocation, mkdir (uutils coreutils 0.8.0, as shipped by Ubuntu 26.04 LTS) can exit 0 even though the kernel returned EEXIST for the underlying mkdir(2) syscall. Two processes racing to create the same directory then both believe they succeeded — which silently breaks the classic mkdir-as-mutex shell pattern (until mkdir lock; do sleep 1; done).

Environment

  • mkdir (uutils coreutils) 0.8.0 — Ubuntu 26.04 LTS default (/usr/bin/mkdir → ../lib/cargo/bin/coreutils/mkdir)
  • Local tmpfs; also reproduced on ext4
  • Observed on two independent machines (2026-08)

Reproduction

# one round: 20 concurrent attempts to create the same directory
rm -rf /tmp/lockdir
for i in $(seq 20); do
  ( mkdir /tmp/lockdir 2>/dev/null && echo "WINNER $i" ) &
done
wait

Expected: exactly one WINNER per round.
Observed: occasionally two winners — measured 3 of 100 rounds with 20 contenders. The rate grows with contention: an independent rerun with 100 contenders per round hit a double-winner in 17 of 20 rounds.

Evidence that the syscall failed but the exit code lied

strace on a losing process shows the kernel refusing the create, yet the process exits 0:

mkdir("/tmp/lockdir", 0777) = -1 EEXIST (File exists)
...
exit_group(0)

Sequential (non-concurrent) invocation reports EEXIST correctly with exit 1, so this looks like an internal race window in the binary rather than a general semantics change.

Controls (0 failures in 100 rounds each, same scenario)

  • busybox mkdir
  • python3 -c 'import os; os.mkdir(...)'
  • bash noclobber ( set -C; : > lock ) (open(O_CREAT|O_EXCL) without an external binary)

GNU coreutils 8.30 on another machine in the same fleet also showed 0 failures.

Impact

Any script using mkdir as an atomic mutex (a pattern old enough to be in textbooks) gets a silently leaky lock on affected systems: two writers enter the critical section and, in our case, interleaved bytes in an append-only file. We have since migrated our protocol to shell noclobber, but the binary's exit code disagreeing with the syscall result seems worth fixing upstream.

Happy to provide the full test harness (100-round concurrency bench) if useful.

Activity

  1. sylvestre commented on Aug 16, 2026

    @sylvestre
    SponsorContributor

    Could you please try with main or 0.10.0?

  2. added a commit that references this issue on Aug 18, 2026
    eb057b9
  3. added 3 commits that reference this issue on Aug 27, 2026
    fb0b72e
    9ee162e
    72d4b7e
  4. added a commit that references this issue on Aug 28, 2026
    c2c9cc6
  5. added a commit that references this issue on Aug 28, 2026
    bf423f3
  6. added a commit that references this issue on Sep 24, 2026
    d97469b
  7. added a commit that references this issue on Sep 28, 2026
    6964363
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions