Public bug reported:
[Impact]
On Ubuntu 26.04, `/usr/bin/mkdir` is uutils coreutils 0.10.0
(`rust-coreutils` 0.10.0-1ubuntu2~26.04.1, through `coreutils-from-uutils`).
When several processes run plain `mkdir DIR` (no `-p`) on the same new path at
the same time, more than one can exit 0. The kernel lets only one `mkdir(2)`
succeed, but a loser exits 0 when its own `mkdir(2)` returns `EEXIST`.
This breaks the common shell locking idiom
until mkdir "$lockdir" 2>/dev/null; do sleep 0.1; done
...critical section...
rmdir "$lockdir"
which POSIX and GNU coreutils support: `mkdir` without `-p` must fail if the
directory exists. On 26.04, two processes can be in the critical section at
once. We saw this as silent data corruption: a port-allocation script that
used a mkdir lock handed the same port to two callers and lost registry
entries under concurrency. Sequential use is unaffected, which is why it goes
unnoticed.
Cause (0.10.0, `src/uu/mkdir/src/mkdir.rs`): `create_dir` checks
`path.exists()` first, then `create_single_dir` calls `DirBuilder::create`. A
failure is mapped to success by `Err(_) if path.is_dir() => Ok(())` even when
not recursive. If another process creates the directory between the
`exists()` check and `mkdir(2)`, `mkdir(2)` fails with `EEXIST`, `is_dir()` is
now true, and `mkdir` exits 0.
Fixed upstream by commit bf423f3 ("mkdir: do not ignore EEXIST on
non-recursive creation"), for uutils/coreutils#13970, released in 0.11.0.
Ubuntu 26.10 (0.12.0) has the fix.
[Test Plan]
Reproducer (only touches a mktemp directory). Each round, N processes run
`mkdir` on one fresh path at once, and it counts the rounds where more than one
exited 0:
```bash
#!/bin/bash
N=${1:-20} R=${2:-100} M=${3:-mkdir}
D=$(mktemp -d) bad=0 r=0
while [ $r -lt $R ]; do
i=0
while [ $i -lt $N ]; do ("$M" "$D/lock" 2>/dev/null && echo win) & i=$((i +
1)); done > "$D/out"
wait
[ "$(grep -c win "$D/out")" -gt 1 ] && bad=$((bad + 1))
rmdir "$D/lock"; r=$((r + 1))
done
rm -rf "$D"
echo "$("$M" --version | head -n 1): $bad of $R rounds had more than one winner
($N contenders)"
```
On Ubuntu 26.04.1 (kernel 7.0.0-38-generic, x86_64, tmpfs `/tmp`):
```
$ ./repro.sh 20 100
mkdir (uutils coreutils) 0.10.0: 4 of 100 rounds had more than one winner (20
contenders)
$ ./repro.sh 20 100 /usr/bin/gnumkdir
mkdir (GNU coreutils) 9.7: 0 of 100 rounds had more than one winner (20
contenders)
```
strace of three concurrent `mkdir` processes on one path, in a round that
failed: the kernel returned success once and `EEXIST` once (the third process
stopped at the `exists()` check and correctly exited 1), yet two processes
exited 0:
```
mkdir("…/x", 0777) = 0
mkdir("…/x", 0777) = -1 EEXIST (File exists)
exit_group(0) ×2, exit_group(1) ×1 (condensed; one line per process in the
trace)
```
Expected after the fix: 0 rounds with more than one winner, and plain
`mkdir existing-dir` still exits 1 with "File exists". `mkdir -p` on an existing
directory still exits 0. Upstream's test `test_mkdir_concurrent_non_recursive`
covers the race.
[Where problems could occur]
The change only affects plain `mkdir` (without `-p`) when `mkdir(2)` returns
`EEXIST`: it now reports the error, as GNU does, instead of exiting 0. A caller
that relied on the buggy success would now see exit 1 with "File exists". But
the same call already fails whenever the directory existed before `mkdir`
started (the `exists()` pre-check), so such callers already handle that
failure. `mkdir -p` and the creation of parent directories are unchanged.
[Other Info]
- Upstream issue: https://github.com/uutils/coreutils/issues/13970
- Upstream fix: https://github.com/uutils/coreutils/commit/bf423f388d54
- Affected: rust-coreutils 0.10.0-1ubuntu2~26.04.1 (resolute-updates). 26.04
shipped 0.8.0, which the upstream issue reports as affected too.
- Workaround for scripts: claim a lock with an exclusive create
(`set -C; : > "$lock"`, which opens with `O_EXCL`), `flock`, or
`/usr/bin/gnumkdir`. Don't rely on `mkdir`'s exit status.
** Affects: rust-coreutils (Ubuntu)
Importance: Undecided
Status: New
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2171333
Title:
[SRU] mkdir exits 0 when another process creates the directory
concurrently (breaks mkdir-as-lock); please backport upstream bf423f3
to 26.04
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/rust-coreutils/+bug/2171333/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs