bug(cp): race condition possible when --backup= or -b and after bypassing the same file check, it leads to stat the same file twice
Open
Nobody has claimed this yet.
U - cp
- Dominant language
- Rust
- Stars
- 24.1k
- Forks
- 2k
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 365
Description
Hi, uutils mainteners
possibly there was a race condition when
relunsec@relunsec:~/software/coreutils/target/debug$ strace ./cp --backup=nil ./a a
execve("./cp", ["./cp", "--backup=nil", "./a", "a"], 0x7ffe2b1d81f8 /* 74 vars */) = 0
brk(NULL) = 0x5f65dabe7000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7dd457ae5000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=97721, ...}) = 0
mmap(NULL, 97721, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7dd457acd000
close(3) = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=187120, ...}) = 0
mmap(NULL, 185256, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7dd457a9f000
mmap(0x7dd457aa3000, 147456, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x4000) = 0x7dd457aa3000
mmap(0x7dd457ac7000, 16384, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7dd457ac7000
mmap(0x7dd457acb000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2c000) = 0x7dd457acb000
close(3) = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1198376, ...}) = 0
mmap(NULL, 1200152, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7dd457979000
mmap(0x7dd45798b000, 655360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x12000) = 0x7dd45798b000
mmap(0x7dd457a2b000, 466944, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xb2000) = 0x7dd457a2b000
mmap(0x7dd457a9d000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x123000) = 0x7dd457a9d000
close(3) = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \250\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 840, 64) = 840
fstat(3, {st_mode=S_IFREG|0755, st_size=2186512, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 840, 64) = 840
mmap(NULL, 2231696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7dd457758000
mmap(0x7dd457780000, 1671168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7dd457780000
mmap(0x7dd457918000, 319488, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7dd457918000
mmap(0x7dd457966000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20d000) = 0x7dd457966000
mmap(0x7dd45796c000, 52624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7dd45796c000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7dd457756000
arch_prctl(ARCH_SET_FS, 0x7dd457756d80) = 0
set_tid_address(0x7dd4577573a8) = 117069
set_robust_list(0x7dd457757060, 24) = 0
rseq(0x7dd457756ac0, 0x21, 0, 0x53053053) = 0
mprotect(0x7dd457966000, 16384, PROT_READ) = 0
mprotect(0x7dd457a9d000, 4096, PROT_READ) = 0
mprotect(0x7dd457acb000, 4096, PROT_READ) = 0
mprotect(0x5f65b4f8b000, 196608, PROT_READ) = 0
mprotect(0x7dd457b2a000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
getrandom("\x81\x98\xb5\x1c\x50\x55\x94\xb4", 8, GRND_NONBLOCK) = 8
munmap(0x7dd457acd000, 97721) = 0
fcntl(0, F_GETFD) = 0
fcntl(1, F_GETFD) = 0
fcntl(2, F_GETFD) = 0
rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7dd45779dcb0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
brk(NULL) = 0x5f65dabe7000
brk(0x5f65dac08000) = 0x5f65dac08000
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "5f65b4b39000-5f65b4cd7000 r--p 0"..., 1024) = 1024
read(3, "--p 0020d000 00:1e 447426 "..., 1024) = 1024
read(3, "7dd457acb000 r--p 00028000 00:1e"..., 1024) = 1024
read(3, ":1e 447420 /"..., 1024) = 412
lseek(3, 0, SEEK_CUR) = 3484
lseek(3, 3400, SEEK_SET) = 3400
close(3) = 0
sched_getaffinity(117069, 32, [0 1 2 3 4 5 6 7]) = 8
rt_sigaction(SIGSEGV, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7dd457ae2000
mprotect(0x7dd457ae2000, 4096, PROT_NONE) = 0
sigaltstack({ss_sp=0x7dd457ae3000, ss_flags=0, ss_size=8192}, NULL) = 0
gettid() = 117069
rt_sigaction(SIGSEGV, {sa_handler=0x5f65b4f69380, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7dd45779dcb0}, NULL, 8) = 0
rt_sigaction(SIGBUS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0x5f65b4f69380, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7dd45779dcb0}, NULL, 8) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/../uu/cp/locales", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=4, ...}) = 0
readlink("/home", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src/uucore", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
faccessat2(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/", F_OK, AT_EACCESS) = 0
readlink("/home/relunsec/software/coreutils/src/uu", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src/uu/cp", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src/uu/cp/locales", 0x7ffd1b58a6c0, 1023) = -1 EINVAL (Invalid argument)
statx(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/locales", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=4, ...}) = 0
openat(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/locales/en-US.ftl", O_RDONLY|O_CLOEXEC) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=4113, ...}) = 0
read(3, "# Common strings shared across a"..., 4113) = 4113
read(3, "", 32) = 0
close(3) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/../uu/cp/locales", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=4, ...}) = 0
openat(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/../uu/cp/locales/en-US.ftl", O_RDONLY|O_CLOEXEC) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=8247, ...}) = 0
read(3, "cp-about = Copy SOURCE to DEST, "..., 8247) = 8247
read(3, "", 32) = 0
close(3) = 0
brk(0x5f65dac2a000) = 0x5f65dac2a000
rt_sigaction(SIGPIPE, {sa_handler=SIG_DFL, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7dd45779dcb0}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7dd45779dcb0}, 8) = 0
rt_sigaction(SIGSEGV, {sa_handler=SIG_DFL, sa_mask=~[RTMIN RT_1], sa_flags=SA_RESTORER, sa_restorer=0x7dd45779dcb0}, {sa_handler=0x5f65b4f69380, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7dd45779dcb0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=SIG_DFL, sa_mask=~[RTMIN RT_1], sa_flags=SA_RESTORER, sa_restorer=0x7dd45779dcb0}, {sa_handler=0x5f65b4f69380, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7dd45779dcb0}, 8) = 0
ioctl(1, TCGETS2, {c_iflag=BRKINT|IGNPAR|ICRNL|IXON|IMAXBEL, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_DROPPABLE|MAP_ANONYMOUS, -1, 0) = 0x7dd457ae1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7dd457ae0000
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
getrandom("\x85\x77\x13\xb0\x90\x41\x62\x17\xa8\xc4\x64\x8a\x42\x01\x72\x87\xd5\x12\xfb\x5b\x91\x5a\x4a\xd9\xdc\xb7\xab\x21\xf3\xe5\xdc\x96", 32, 0) = 32
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "./a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "./a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "./a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
newfstatat(AT_FDCWD, "./a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
statx(AT_FDCWD, "./a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
newfstatat(AT_FDCWD, "./a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
statx(AT_FDCWD, "a.~1~", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, 0x7ffd1b588060) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
newfstatat(AT_FDCWD, "a~", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
openat(AT_FDCWD, "a", O_RDONLY|O_CLOEXEC) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
openat(AT_FDCWD, "a~", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0100664) = 4
statx(4, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=0, ...}) = 0
fchmod(4, 0100664) = 0
copy_file_range(3, NULL, 4, NULL, 1073741824, 0) = 6
copy_file_range(3, NULL, 4, NULL, 1073741824, 0) = 0
close(4) = 0
close(3) = 0
statx(AT_FDCWD, "./a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7dd457ae2000, 12288) = 0
exit_group(0) = ?
+++ exited with 0 +++
it calls statx syscall twice, against the same file which leaves a massive ms of race condition possibility, because statx multiple times against the same file to a be swapped, the gnu one
relunsec@relunsec:~/software/coreutils/target/debug$ gnucp --backup=nil ./a a
gnucp: './a' and 'a' are the same file
correctly rejects that
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the reported cp --backup=nil ./a a command with strace. Then inspect the cp handling for --backup or -b around the repeated stat calls for a and ./a. Done means the suspected race is confirmed and the issue has a concrete, testable change target.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 42/100