The Pedigree Project 0.1
fanotify-handle-contract-test/lifetime.c
1#define _GNU_SOURCE
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/ioctl.h>
8#include <sys/socket.h>
9#include <sys/stat.h>
10
11static int descriptor_surface(void) {
12 int failed = 0, group = -1, duplicate = -1, enabled;
13 struct stat state, copy;
14 char path[96], target[96];
15 group = fh_group(1);
16 CHECK(group >= 0);
17 duplicate = dup(group);
18 CHECK(duplicate >= 0 && !fstat(group, &state) && !fstat(duplicate, &copy));
19 CHECK(state.st_ino && state.st_ino == copy.st_ino && state.st_nlink == 1 && state.st_size == 0);
20 CHECK((state.st_mode & 0777) == 0600);
21 CHECK((fcntl(group, F_GETFL) & (O_ACCMODE | O_NONBLOCK)) == (O_RDWR | O_NONBLOCK));
22 CHECK((fcntl(group, F_GETFD) & FD_CLOEXEC) && !(fcntl(duplicate, F_GETFD) & FD_CLOEXEC));
23 enabled = 0;
24 CHECK(!ioctl(duplicate, FIONBIO, &enabled) && !(fcntl(group, F_GETFL) & O_NONBLOCK));
25 enabled = 1;
26 CHECK(!ioctl(group, FIONBIO, &enabled) && (fcntl(duplicate, F_GETFL) & O_NONBLOCK));
27 CHECK(!ioctl(duplicate, FIOCLEX) && (fcntl(duplicate, F_GETFD) & FD_CLOEXEC));
28 CHECK(!ioctl(duplicate, FIONCLEX) && !(fcntl(duplicate, F_GETFD) & FD_CLOEXEC));
29 CHECK(lseek(group, 17, SEEK_SET) == 0 && lseek(group, -2, SEEK_CUR) == 0);
30 errno = 0;
31 CHECK(lseek(group, 0, 123) == -1 && errno == EINVAL);
32 errno = 0;
33 CHECK(write(group, "x", 1) == -1 && errno == EINVAL);
34 errno = 0;
35 CHECK(ioctl(group, 0x7fff, NULL) == -1 && errno == ENOTTY);
36 snprintf(path, sizeof(path), "/proc/self/fd/%d", group);
37 ssize_t length = readlink(path, target, sizeof(target));
38 CHECK(length == (ssize_t)strlen("anon_inode:[fanotify]"));
39 CHECK(!memcmp(target, "anon_inode:[fanotify]", (size_t)length));
40out:
41 if (duplicate >= 0)
42 close(duplicate);
43 if (group >= 0)
44 close(group);
45 return failed;
46}
47
48static int queued_transport(void) {
49 int failed = 0, group = -1, duplicate = -1, received = -1, sockets[2] = {-1, -1};
50 struct fh_file file = {.fd = -1};
51 CHECK(!fh_create(&file));
52 group = fh_group(1);
53 CHECK(group >= 0 && !fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
54 duplicate = dup(group);
55 CHECK(duplicate >= 0 && !socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
56 CHECK(!fh_send_fd(sockets[0], duplicate));
57 CHECK(!close(group));
58 group = -1;
59 CHECK(!close(duplicate));
60 duplicate = -1;
61 CHECK(!fh_modify(&file));
62 received = fh_receive_fd(sockets[1]);
63 CHECK(received >= 0 && (fcntl(received, F_GETFD) & FD_CLOEXEC));
64 CHECK(!fh_event(received, &file, FAN_MODIFY, getpid()));
65 CHECK(!fh_modify(&file));
66 CHECK(!unlink(file.path));
67 file.path[0] = 0;
68 CHECK(!fh_event(received, &file, FAN_MODIFY, getpid()));
69 errno = 0;
70 CHECK(open_by_handle_at(file.fd, (struct file_handle*)&file.handle, O_RDONLY) == -1 &&
71 errno == ESTALE);
72 CHECK(!fh_modify(&file) && fh_readable(received, 0) == 0);
73out:
74 if (received >= 0)
75 close(received);
76 if (duplicate >= 0)
77 close(duplicate);
78 if (group >= 0)
79 close(group);
80 for (int n = 0; n < 2; ++n)
81 if (sockets[n] >= 0)
82 close(sockets[n]);
83 fh_close(&file);
84 return failed;
85}
86
87int fh_exec(int argc, char** argv) {
88 if (argc != 5)
89 return 2;
90 alarm(10);
91 int kept = atoi(argv[2]), dropped = atoi(argv[3]);
92 if (fcntl(kept, F_GETFD) < 0)
93 return 10;
94 errno = 0;
95 if (fcntl(dropped, F_GETFD) != -1 || errno != EBADF)
96 return 11;
97 struct fh_file file = {.fd = -1};
98 file.fd = open(argv[4], O_RDWR | O_CLOEXEC);
99 if (file.fd < 0 || fh_export(file.fd, &file.handle, &file.mount_id))
100 return 12;
101 int result = fh_event(kept, &file, FAN_MODIFY, getppid()) || fh_modify(&file) ||
102 fh_event(kept, &file, FAN_MODIFY, getpid()) ||
103 fh_mark(kept, &file, FAN_MARK_ADD, FAN_ATTRIB);
104 close(file.fd);
105 return result ? 13 : 0;
106}
107
108static int inherited_exec(void) {
109 int failed = 0, group = -1, kept = -1;
110 pid_t child = -1;
111 struct fh_file file = {.fd = -1};
112 CHECK(!fh_create(&file));
113 group = fh_group(1);
114 CHECK(group >= 0 && !fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
115 kept = dup(group);
116 CHECK(kept >= 0 && !fh_modify(&file));
117 child = fork();
118 CHECK(child >= 0);
119 if (!child) {
120 char keep_text[32], drop_text[32];
121 snprintf(keep_text, sizeof(keep_text), "%d", kept);
122 snprintf(drop_text, sizeof(drop_text), "%d", group);
123 execl(FH_APP, FH_APP, "fanotify-exec", keep_text, drop_text, file.path, (char*)NULL);
124 _exit(20);
125 }
126 CHECK(!fh_reap(child, 12000));
127 child = -1;
128 CHECK(fh_readable(group, 0) == 0);
129 CHECK(!fchmod(file.fd, 0644));
130 CHECK(!fh_event(group, &file, FAN_ATTRIB, getpid()));
131 CHECK(!close(kept));
132 kept = -1;
133 CHECK(!fh_modify(&file) && !fh_event(group, &file, FAN_MODIFY, getpid()));
134out:
135 if (child > 0)
136 fh_reap(child, 100);
137 if (kept >= 0)
138 close(kept);
139 if (group >= 0)
140 close(group);
141 fh_close(&file);
142 return failed;
143}
144int fh_lifetime(void) {
145 return descriptor_surface() || queued_transport() || inherited_exec();
146}