13static int aliases(
int backend) {
14 int failed = 0, alias = -1, duplicate = -1, reused = -1;
16 struct xa_file file = {.fd = -1}, replacement = {.fd = -1};
17 struct xa_file retained = {.fd = -1};
18 char moved[224] = {0};
19 CHECK(!xa_create(&file, backend, 0));
20 CHECK(!fsetxattr(file.fd,
"user.slot",
"old", 3, 0));
21 CHECK((alias = xa_open_alias(&file)) >= 0);
22 CHECK((duplicate = dup(file.fd)) >= 0);
24 retained.fd = duplicate;
25 struct stat first, second;
26 CHECK(!fstat(file.fd, &first) && !fstat(alias, &second));
27 CHECK(first.st_ino == second.st_ino && first.st_dev == second.st_dev);
28 CHECK(!fsetxattr(alias,
"user.slot",
"alias", 5, XATTR_REPLACE));
29 CHECK(!xa_value(&retained, XA_FD,
"user.slot",
"alias", 5));
30 CHECK((child = fork()) >= 0);
32 int result = xa_value(&retained, XA_FD,
"user.slot",
"alias", 5) ||
33 fsetxattr(duplicate,
"user.slot",
"child", 5, XATTR_REPLACE);
34 _exit(result ? 1 : 0);
36 int result = xa_reap(child, 8000);
39 CHECK(!xa_value(&file, XA_FD,
"user.slot",
"child", 5));
41 snprintf(moved,
sizeof(moved),
"%s.moved", file.path);
42 CHECK(!rename(file.path, moved));
45 CHECK(getxattr(moved,
"user.slot", value,
sizeof(value)) == 5 && !memcmp(value,
"child", 5));
46 CHECK(!unlink(moved));
49 CHECK(!xa_create(&replacement, XA_MEMFD, 0));
50 CHECK(!fsetxattr(replacement.fd,
"user.slot",
"new", 3, 0));
52 CHECK(!close(file.fd));
54 CHECK((reused = dup2(replacement.fd, number)) == number);
55 struct xa_file current = replacement;
57 CHECK(!xa_value(¤t, XA_FD,
"user.slot",
"new", 3));
58 CHECK(!xa_value(&retained, XA_FD,
"user.slot",
"child", 5));
61 CHECK(!fsetxattr(duplicate,
"user.slot",
"last", 4, XATTR_REPLACE));
62 CHECK(!xa_value(&retained, XA_FD,
"user.slot",
"last", 4));
76 xa_close(&replacement);
81static int send_descriptor(
int socket,
int fd) {
83 struct iovec vector = {&byte, 1};
85 struct cmsghdr aligned;
86 unsigned char bytes[CMSG_SPACE(
sizeof(
int))];
88 struct msghdr
message = {.msg_iov = &vector,
90 .msg_control = control.bytes,
91 .msg_controllen =
sizeof(control.bytes)};
92 struct cmsghdr* header = CMSG_FIRSTHDR(&
message);
93 header->cmsg_level = SOL_SOCKET;
94 header->cmsg_type = SCM_RIGHTS;
95 header->cmsg_len = CMSG_LEN(
sizeof(
int));
96 memcpy(CMSG_DATA(header), &fd,
sizeof(fd));
97 return sendmsg(socket, &
message, 0) == 1 ? 0 : -1;
100static int receive_descriptor(
int socket) {
102 struct iovec vector = {&byte, 1};
104 struct cmsghdr aligned;
105 unsigned char bytes[CMSG_SPACE(
sizeof(
int))];
107 struct msghdr
message = {.msg_iov = &vector,
109 .msg_control = control.bytes,
110 .msg_controllen =
sizeof(control.bytes)};
111 if (recvmsg(socket, &
message, MSG_CMSG_CLOEXEC) != 1 ||
byte !=
'f' ||
112 (
message.msg_flags & MSG_CTRUNC))
114 struct cmsghdr* header = CMSG_FIRSTHDR(&
message);
115 if (!header || header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS ||
116 header->cmsg_len != CMSG_LEN(
sizeof(
int)))
119 memcpy(&fd, CMSG_DATA(header),
sizeof(fd));
123static int queued_descriptor(
int backend) {
124 int failed = 0, sockets[2] = {-1, -1}, ready[2] = {-1, -1}, gate[2] = {-1, -1};
126 struct xa_file file = {.fd = -1};
127 CHECK(!xa_create(&file, backend, 0));
128 CHECK(!fsetxattr(file.fd,
"user.queued",
"queued", 6, 0));
129 CHECK(!socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets));
130 CHECK(!pipe(ready) && !pipe(gate));
131 CHECK((child = fork()) >= 0);
137 if (xa_send(ready[1],
'r') || xa_receive(gate[0],
'g'))
139 file.fd = receive_descriptor(sockets[1]);
140 if (file.fd < 0 || xa_value(&file, XA_FD,
"user.queued",
"queued", 6) ||
141 fsetxattr(file.fd,
"user.queued",
"received", 8, XATTR_REPLACE) ||
142 xa_value(&file, XA_FD,
"user.queued",
"received", 8))
153 CHECK(!xa_receive(ready[0],
'r'));
154 CHECK(!send_descriptor(sockets[0], file.fd));
156 CHECK(!xa_send(gate[1],
'g'));
157 int result = xa_reap(child, 8000);
162 kill(child, SIGKILL);
163 xa_reap(child, 1000);
165 for (
int n = 0; n < 2; ++n) {
177static int rename_replacement(
void) {
179 struct xa_file source = {.fd = -1}, victim = {.fd = -1};
180 CHECK(!xa_create(&source, XA_EXT2, 0));
181 CHECK(!xa_create(&victim, XA_EXT2, 0));
182 CHECK(!fsetxattr(source.fd,
"user.inode",
"source", 6, 0));
183 CHECK(!fsetxattr(victim.fd,
"user.inode",
"victim", 6, 0));
184 CHECK(!rename(source.path, victim.path));
186 CHECK(!xa_value(&victim, XA_PATH,
"user.inode",
"source", 6));
187 CHECK(!xa_value(&victim, XA_FD,
"user.inode",
"victim", 6));
188 CHECK(!fremovexattr(victim.fd,
"user.inode"));
189 CHECK(!xa_value(&source, XA_FD,
"user.inode",
"source", 6));
196static int sealed_memfd(
void) {
198 struct xa_file file = {.fd = -1};
199 const int seals = F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_FUTURE_WRITE;
200 CHECK(!xa_create(&file, XA_MEMFD, 0));
201 CHECK(write(file.fd,
"content", 7) == 7);
202 CHECK(!fsetxattr(file.fd,
"user.sealed",
"before", 6, 0));
203 CHECK(!fcntl(file.fd, F_ADD_SEALS, seals));
204 CHECK(fcntl(file.fd, F_GET_SEALS) == seals);
205 CHECK(pwrite(file.fd,
"x", 1, 0) == -1 && errno == EPERM);
206 CHECK(!fsetxattr(file.fd,
"user.sealed",
"after", 5, XATTR_REPLACE));
207 CHECK(!xa_value(&file, XA_FD,
"user.sealed",
"after", 5));
208 CHECK(!fsetxattr(file.fd,
"user.new", NULL, 0, XATTR_CREATE));
209 CHECK(!fremovexattr(file.fd,
"user.sealed"));
210 const char* names[] = {
"user.new"};
211 CHECK(!xa_names(&file, XA_FD, names, 1));
217int xa_lifetime(
void) {
218 for (
int backend = XA_MEMFD; backend <= XA_EXT2; ++backend)
219 if (aliases(backend) || queued_descriptor(backend))
221 return rename_replacement() || sealed_memfd();