Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: call dup2() syscall directly

We've ran into issues with using dup2() API in production setting, where
libbpf is linked into large production environment and ends up calling
unintended custom implementations of dup2(). These custom implementations
don't provide atomic FD replacement guarantees of dup2() syscall,
leading to subtle and hard to debug issues.

To prevent this in the future and guarantee that no libc implementation
will do their own custom non-atomic dup2() implementation, call dup2()
syscall directly with syscall(SYS_dup2).

Note that some architectures don't seem to provide dup2 and have dup3
instead. Try to detect and pick best syscall.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Song Liu <song@kernel.org>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20240119210201.1295511-1-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
bc308d01 c80c6434

+11 -1
+11 -1
tools/lib/bpf/libbpf_internal.h
··· 15 15 #include <linux/err.h> 16 16 #include <fcntl.h> 17 17 #include <unistd.h> 18 + #include <sys/syscall.h> 18 19 #include <libelf.h> 19 20 #include "relo_core.h" 20 21 ··· 556 555 return fd; 557 556 } 558 557 558 + static inline int sys_dup2(int oldfd, int newfd) 559 + { 560 + #ifdef __NR_dup2 561 + return syscall(__NR_dup2, oldfd, newfd); 562 + #else 563 + return syscall(__NR_dup3, oldfd, newfd, 0); 564 + #endif 565 + } 566 + 559 567 /* Point *fixed_fd* to the same file that *tmp_fd* points to. 560 568 * Regardless of success, *tmp_fd* is closed. 561 569 * Whatever *fixed_fd* pointed to is closed silently. ··· 573 563 { 574 564 int err; 575 565 576 - err = dup2(tmp_fd, fixed_fd); 566 + err = sys_dup2(tmp_fd, fixed_fd); 577 567 err = err < 0 ? -errno : 0; 578 568 close(tmp_fd); /* clean up temporary FD */ 579 569 return err;