18#include <sys/syscall.h>
22enum { Dup3StressIterations = 256, Dup3WaitAttempts = 100000 };
29 volatile int finished;
31 volatile int observations;
34static int wait_for_value(
volatile int* value,
int expected) {
35 for (
size_t attempt = 0; attempt < Dup3WaitAttempts; ++attempt) {
36 if (__atomic_load_n(value, __ATOMIC_ACQUIRE) == expected)
43static void* replace_cloexec_descriptor(
void* parameter) {
45 __atomic_add_fetch(&context->ready, 1, __ATOMIC_RELEASE);
46 if (wait_for_value(&context->start, 1)) {
47 __atomic_store_n(&context->failed, 1, __ATOMIC_RELEASE);
48 __atomic_store_n(&context->finished, 1, __ATOMIC_RELEASE);
52 for (
size_t iteration = 0; iteration < Dup3StressIterations; ++iteration) {
53 if (syscall(SYS_dup3, context->source, context->target, O_CLOEXEC) != context->target) {
54 __atomic_store_n(&context->failed, 1, __ATOMIC_RELEASE);
59 __atomic_store_n(&context->finished, 1, __ATOMIC_RELEASE);
63static void* observe_cloexec_descriptor(
void* parameter) {
65 __atomic_add_fetch(&context->ready, 1, __ATOMIC_RELEASE);
66 if (wait_for_value(&context->start, 1)) {
67 __atomic_store_n(&context->failed, 1, __ATOMIC_RELEASE);
72 if (fcntl(context->target, F_GETFD) != FD_CLOEXEC) {
73 __atomic_store_n(&context->failed, 1, __ATOMIC_RELEASE);
76 __atomic_add_fetch(&context->observations, 1, __ATOMIC_RELEASE);
78 }
while (!__atomic_load_n(&context->finished, __ATOMIC_ACQUIRE));
85 void (*previousSigpipe)(int) = signal(SIGPIPE, SIG_IGN);
87 puts(
"Testing dup3 descriptor replacement... ");
89 if (previousSigpipe == SIG_ERR || pipe(sourcePipe) || pipe(displacedPipe) ||
90 fcntl(sourcePipe[0], F_SETFD, FD_CLOEXEC))
94 if (syscall(SYS_dup3, sourcePipe[0], displacedPipe[0], 0) != displacedPipe[0] || errno != 173 ||
95 fcntl(sourcePipe[0], F_GETFD) != FD_CLOEXEC || fcntl(displacedPipe[0], F_GETFD) != 0)
99 if (write(displacedPipe[1],
"x", 1) != -1 || errno != EPIPE || close(displacedPipe[1]))
102 if (write(sourcePipe[1],
"a", 1) != 1)
105 if (read(displacedPipe[0], &
byte, 1) != 1 ||
byte !=
'a')
108 if (syscall(SYS_dup3, sourcePipe[0], displacedPipe[0], O_CLOEXEC) != displacedPipe[0] ||
109 fcntl(displacedPipe[0], F_GETFD) != FD_CLOEXEC)
113 if (syscall(SYS_dup3, sourcePipe[0], sourcePipe[0], 0) != -1 || errno != EINVAL ||
114 fcntl(sourcePipe[0], F_GETFD) != FD_CLOEXEC)
117 if (syscall(SYS_dup3, -1, -1, 0) != -1 || errno != EINVAL)
120 if (syscall(SYS_dup3, sourcePipe[0], displacedPipe[0], O_NONBLOCK) != -1 || errno != EINVAL ||
121 fcntl(displacedPipe[0], F_GETFD) != FD_CLOEXEC)
124 if (syscall(SYS_dup3, -1, displacedPipe[0], 0) != -1 || errno != EBADF ||
125 fcntl(displacedPipe[0], F_GETFD) != FD_CLOEXEC)
128 if (syscall(SYS_dup3, sourcePipe[0], -1, 0) != -1 || errno != EBADF)
131 if (syscall(SYS_dup3, sourcePipe[0], 16384, 0) != -1 || errno != EBADF)
134 if (write(sourcePipe[1],
"b", 1) != 1 || read(displacedPipe[0], &
byte, 1) != 1 ||
byte !=
'b')
138 .source = sourcePipe[0],
139 .target = displacedPipe[0],
143 if (pthread_create(&replacer, 0, replace_cloexec_descriptor, &stress))
145 if (pthread_create(&observer, 0, observe_cloexec_descriptor, &stress)) {
146 __atomic_store_n(&stress.start, 1, __ATOMIC_RELEASE);
147 pthread_join(replacer, 0);
150 if (wait_for_value(&stress.ready, 2)) {
151 __atomic_store_n(&stress.start, 1, __ATOMIC_RELEASE);
152 pthread_join(replacer, 0);
153 pthread_join(observer, 0);
156 __atomic_store_n(&stress.start, 1, __ATOMIC_RELEASE);
157 if (pthread_join(replacer, 0) || pthread_join(observer, 0) ||
158 __atomic_load_n(&stress.failed, __ATOMIC_ACQUIRE) ||
159 !__atomic_load_n(&stress.observations, __ATOMIC_ACQUIRE))
162 if (close(sourcePipe[0]) || write(sourcePipe[1],
"c", 1) != 1 ||
163 read(displacedPipe[0], &
byte, 1) != 1 ||
byte !=
'c' || close(displacedPipe[0]) ||
164 close(sourcePipe[1]) || signal(SIGPIPE, previousSigpipe) == SIG_ERR)