16#define CHECK(condition) \
19 fprintf(stderr, "%s:%d: %s (errno=%d)\n", __FILE__, __LINE__, #condition, errno); \
25static int toggle(
int fd) {
26 int failed = 0, alias = -1, status = fcntl(fd, F_GETFL);
27 CHECK(status >= 0 && !fcntl(fd, F_SETFD, 0));
28 CHECK((alias = dup(fd)) >= 0 && fcntl(alias, F_GETFD) == 0);
30 CHECK(!ioctl(fd, FIOCLEX, (
void*)(uintptr_t)1));
31 CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC && fcntl(alias, F_GETFD) == 0);
32 CHECK(!ioctl(fd, FIOCLEX, NULL) && fcntl(fd, F_GETFD) == FD_CLOEXEC);
33 CHECK(!ioctl(alias, FIOCLEX, NULL));
34 CHECK(!ioctl(fd, FIONCLEX, (
void*)(uintptr_t)1));
35 CHECK(fcntl(fd, F_GETFD) == 0 && fcntl(alias, F_GETFD) == FD_CLOEXEC);
36 CHECK(!ioctl(fd, FIONCLEX, NULL) && fcntl(fd, F_GETFD) == 0);
37 CHECK(!ioctl(alias, FIONCLEX, NULL) && fcntl(alias, F_GETFD) == 0);
38 CHECK(fcntl(fd, F_GETFL) == status && fcntl(alias, F_GETFL) == status);
45static int regular(
void) {
46 int failed = 0, fd = -1;
47 char path[] =
"/tmp/descriptor-cloexec-XXXXXX";
48 CHECK((fd = mkstemp(path)) >= 0);
58static int pipes(
void) {
59 int failed = 0, pair[2] = {-1, -1};
61 CHECK(!toggle(pair[0]) && !toggle(pair[1]));
63 for (
int n = 0; n < 2; ++n)
69static int sockets(
void) {
70 int failed = 0, pair[2] = {-1, -1};
71 CHECK(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
72 CHECK(!toggle(pair[0]) && !toggle(pair[1]));
74 for (
int n = 0; n < 2; ++n)
80static int validation(
void) {
81 int failed = 0, path = -1;
82 CHECK(ioctl(-1, FIOCLEX, NULL) == -1 && errno == EBADF);
83 CHECK(ioctl(-1, FIONCLEX, NULL) == -1 && errno == EBADF);
84 CHECK((path = open(
"/", O_PATH | O_DIRECTORY)) >= 0);
85 CHECK(ioctl(path, FIOCLEX, NULL) == -1 && errno == EBADF);
86 CHECK(ioctl(path, FIONCLEX, NULL) == -1 && errno == EBADF);
88 CHECK(!fcntl(path, F_SETFD, FD_CLOEXEC) && fcntl(path, F_GETFD) == FD_CLOEXEC);
89 CHECK(!fcntl(path, F_SETFD, 0) && fcntl(path, F_GETFD) == 0);
96static int exec_child(
const char* closed_arg,
const char* kept_arg) {
97 int failed = 0, closed = atoi(closed_arg), kept = atoi(kept_arg);
100 CHECK(closed >= 64 && kept > closed);
101 CHECK(fcntl(closed, F_GETFD) == -1 && errno == EBADF);
102 CHECK(fcntl(kept, F_GETFD) == 0);
103 CHECK(read(kept, content,
sizeof(content)) == (ssize_t)
sizeof(content));
104 CHECK(!memcmp(content,
"closure",
sizeof(content)));
110static int exec_closure(
const char* executable) {
111 int failed = 0, fd = -1, closed = -1, kept = -1, status = -1;
113 char path[] =
"/tmp/descriptor-cloexec-exec-XXXXXX";
114 CHECK((fd = mkstemp(path)) >= 0);
115 CHECK(write(fd,
"closure", 7) == 7 && lseek(fd, 0, SEEK_SET) == 0);
117 CHECK((closed = fcntl(fd, F_DUPFD, 64)) >= 64);
118 CHECK((kept = fcntl(fd, F_DUPFD, closed + 1)) > closed);
119 CHECK(!ioctl(closed, FIOCLEX, NULL));
120 CHECK(!ioctl(kept, FIOCLEX, NULL) && !ioctl(kept, FIONCLEX, NULL));
121 CHECK((child = fork()) >= 0);
123 char closed_arg[24], kept_arg[24];
124 snprintf(closed_arg,
sizeof(closed_arg),
"%d", closed);
125 snprintf(kept_arg,
sizeof(kept_arg),
"%d", kept);
126 char* arguments[] = {(
char*)executable,
"--exec", closed_arg, kept_arg, NULL};
130 execv(executable, arguments);
133 for (
int n = 0; n < 1000; ++n) {
134 pid_t result = waitpid(child, &status, WNOHANG);
135 if (result == child) {
139 CHECK(result == 0 || (result < 0 && errno == EINTR));
140 struct timespec pause = {0, 10000000};
141 nanosleep(&pause, NULL);
143 CHECK(child == -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0);
144 CHECK(fcntl(closed, F_GETFD) == FD_CLOEXEC && fcntl(kept, F_GETFD) == 0);
147 kill(child, SIGKILL);
148 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
162int main(
int argc,
char** argv) {
163 if (argc == 4 && !strcmp(argv[1],
"--exec"))
164 return exec_child(argv[2], argv[3]);
165 if (argc != 1 || !argv[0] || argv[0][0] !=
'/' || !argv[0][1]) {
166 fputs(
"Invoke this contract test using its absolute executable path.\n", stderr);
169 static const struct {
173 {
"regular", regular}, {
"pipe", pipes}, {
"socket", sockets}, {
"validation", validation}};
174 for (
unsigned n = 0; n <
sizeof(cases) /
sizeof(cases[0]); ++n) {
175 printf(
"DESCRIPTOR-CLOEXEC: BEGIN %s\n", cases[n].name);
177 int result = cases[n].test();
178 printf(
"DESCRIPTOR-CLOEXEC: %s %s status=%d\n", result ?
"FAIL" :
"PASS", cases[n].name,
184 puts(
"DESCRIPTOR-CLOEXEC: BEGIN exec-closure");
186 int result = exec_closure(argv[0]);
187 printf(
"DESCRIPTOR-CLOEXEC: %s exec-closure status=%d\n", result ?
"FAIL" :
"PASS", result);
190 puts(
"DESCRIPTOR-CLOEXEC: END PASS");