1 // SPDX-License-Identifier: GPL-2.0 OR MIT 2 #define _GNU_SOURCE 3 #include <error.h> 4 #include <limits.h> 5 #include <stddef.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <sys/socket.h> 9 #include <linux/socket.h> 10 #include <unistd.h> 11 #include <string.h> 12 #include <errno.h> 13 #include <sys/un.h> 14 #include <sys/signal.h> 15 #include <sys/types.h> 16 #include <sys/wait.h> 17 18 #include "../../kselftest_harness.h" 19 20 #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 21 #define log_err(MSG, ...) \ 22 fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", __FILE__, __LINE__, \ 23 clean_errno(), ##__VA_ARGS__) 24 25 #ifndef SCM_PIDFD 26 #define SCM_PIDFD 0x04 27 #endif 28 29 static void child_die() 30 { 31 exit(1); 32 } 33 34 static int safe_int(const char *numstr, int *converted) 35 { 36 char *err = NULL; 37 long sli; 38 39 errno = 0; 40 sli = strtol(numstr, &err, 0); 41 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) 42 return -ERANGE; 43 44 if (errno != 0 && sli == 0) 45 return -EINVAL; 46 47 if (err == numstr || *err != '\0') 48 return -EINVAL; 49 50 if (sli > INT_MAX || sli < INT_MIN) 51 return -ERANGE; 52 53 *converted = (int)sli; 54 return 0; 55 } 56 57 static int char_left_gc(const char *buffer, size_t len) 58 { 59 size_t i; 60 61 for (i = 0; i < len; i++) { 62 if (buffer[i] == ' ' || buffer[i] == '\t') 63 continue; 64 65 return i; 66 } 67 68 return 0; 69 } 70 71 static int char_right_gc(const char *buffer, size_t len) 72 { 73 int i; 74 75 for (i = len - 1; i >= 0; i--) { 76 if (buffer[i] == ' ' || buffer[i] == '\t' || 77 buffer[i] == '\n' || buffer[i] == '\0') 78 continue; 79 80 return i + 1; 81 } 82 83 return 0; 84 } 85 86 static char *trim_whitespace_in_place(char *buffer) 87 { 88 buffer += char_left_gc(buffer, strlen(buffer)); 89 buffer[char_right_gc(buffer, strlen(buffer))] = '\0'; 90 return buffer; 91 } 92 93 /* borrowed (with all helpers) from pidfd/pidfd_open_test.c */ 94 static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen) 95 { 96 int ret; 97 char path[512]; 98 FILE *f; 99 size_t n = 0; 100 pid_t result = -1; 101 char *line = NULL; 102 103 snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", pidfd); 104 105 f = fopen(path, "re"); 106 if (!f) 107 return -1; 108 109 while (getline(&line, &n, f) != -1) { 110 char *numstr; 111 112 if (strncmp(line, key, keylen)) 113 continue; 114 115 numstr = trim_whitespace_in_place(line + 4); 116 ret = safe_int(numstr, &result); 117 if (ret < 0) 118 goto out; 119 120 break; 121 } 122 123 out: 124 free(line); 125 fclose(f); 126 return result; 127 } 128 129 static int cmsg_check(int fd) 130 { 131 struct msghdr msg = { 0 }; 132 struct cmsghdr *cmsg; 133 struct iovec iov; 134 struct ucred *ucred = NULL; 135 int data = 0; 136 char control[CMSG_SPACE(sizeof(struct ucred)) + 137 CMSG_SPACE(sizeof(int))] = { 0 }; 138 int *pidfd = NULL; 139 pid_t parent_pid; 140 int err; 141 142 iov.iov_base = &data; 143 iov.iov_len = sizeof(data); 144 145 msg.msg_iov = &iov; 146 msg.msg_iovlen = 1; 147 msg.msg_control = control; 148 msg.msg_controllen = sizeof(control); 149 150 err = recvmsg(fd, &msg, 0); 151 if (err < 0) { 152 log_err("recvmsg"); 153 return 1; 154 } 155 156 if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { 157 log_err("recvmsg: truncated"); 158 return 1; 159 } 160 161 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 162 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 163 if (cmsg->cmsg_level == SOL_SOCKET && 164 cmsg->cmsg_type == SCM_PIDFD) { 165 if (cmsg->cmsg_len < sizeof(*pidfd)) { 166 log_err("CMSG parse: SCM_PIDFD wrong len"); 167 return 1; 168 } 169 170 pidfd = (void *)CMSG_DATA(cmsg); 171 } 172 173 if (cmsg->cmsg_level == SOL_SOCKET && 174 cmsg->cmsg_type == SCM_CREDENTIALS) { 175 if (cmsg->cmsg_len < sizeof(*ucred)) { 176 log_err("CMSG parse: SCM_CREDENTIALS wrong len"); 177 return 1; 178 } 179 180 ucred = (void *)CMSG_DATA(cmsg); 181 } 182 } 183 184 /* send(pfd, "x", sizeof(char), 0) */ 185 if (data != 'x') { 186 log_err("recvmsg: data corruption"); 187 return 1; 188 } 189 190 if (!pidfd) { 191 log_err("CMSG parse: SCM_PIDFD not found"); 192 return 1; 193 } 194 195 if (!ucred) { 196 log_err("CMSG parse: SCM_CREDENTIALS not found"); 197 return 1; 198 } 199 200 /* pidfd from SCM_PIDFD should point to the parent process PID */ 201 parent_pid = 202 get_pid_from_fdinfo_file(*pidfd, "Pid:", sizeof("Pid:") - 1); 203 if (parent_pid != getppid()) { 204 log_err("wrong SCM_PIDFD %d != %d", parent_pid, getppid()); 205 return 1; 206 } 207 208 return 0; 209 } 210 211 struct sock_addr { 212 char sock_name[32]; 213 struct sockaddr_un listen_addr; 214 socklen_t addrlen; 215 }; 216 217 FIXTURE(scm_pidfd) 218 { 219 int server; 220 pid_t client_pid; 221 int startup_pipe[2]; 222 struct sock_addr server_addr; 223 struct sock_addr *client_addr; 224 }; 225 226 FIXTURE_VARIANT(scm_pidfd) 227 { 228 int type; 229 bool abstract; 230 }; 231 232 FIXTURE_VARIANT_ADD(scm_pidfd, stream_pathname) 233 { 234 .type = SOCK_STREAM, 235 .abstract = 0, 236 }; 237 238 FIXTURE_VARIANT_ADD(scm_pidfd, stream_abstract) 239 { 240 .type = SOCK_STREAM, 241 .abstract = 1, 242 }; 243 244 FIXTURE_VARIANT_ADD(scm_pidfd, dgram_pathname) 245 { 246 .type = SOCK_DGRAM, 247 .abstract = 0, 248 }; 249 250 FIXTURE_VARIANT_ADD(scm_pidfd, dgram_abstract) 251 { 252 .type = SOCK_DGRAM, 253 .abstract = 1, 254 }; 255 256 FIXTURE_SETUP(scm_pidfd) 257 { 258 self->client_addr = mmap(NULL, sizeof(*self->client_addr), PROT_READ | PROT_WRITE, 259 MAP_SHARED | MAP_ANONYMOUS, -1, 0); 260 ASSERT_NE(MAP_FAILED, self->client_addr); 261 } 262 263 FIXTURE_TEARDOWN(scm_pidfd) 264 { 265 close(self->server); 266 267 kill(self->client_pid, SIGKILL); 268 waitpid(self->client_pid, NULL, 0); 269 270 if (!variant->abstract) { 271 unlink(self->server_addr.sock_name); 272 unlink(self->client_addr->sock_name); 273 } 274 } 275 276 static void fill_sockaddr(struct sock_addr *addr, bool abstract) 277 { 278 char *sun_path_buf = (char *)&addr->listen_addr.sun_path; 279 280 addr->listen_addr.sun_family = AF_UNIX; 281 addr->addrlen = offsetof(struct sockaddr_un, sun_path); 282 snprintf(addr->sock_name, sizeof(addr->sock_name), "scm_pidfd_%d", getpid()); 283 addr->addrlen += strlen(addr->sock_name); 284 if (abstract) { 285 *sun_path_buf = '\0'; 286 addr->addrlen++; 287 sun_path_buf++; 288 } else { 289 unlink(addr->sock_name); 290 } 291 memcpy(sun_path_buf, addr->sock_name, strlen(addr->sock_name)); 292 } 293 294 static void client(FIXTURE_DATA(scm_pidfd) *self, 295 const FIXTURE_VARIANT(scm_pidfd) *variant) 296 { 297 int err; 298 int cfd; 299 socklen_t len; 300 struct ucred peer_cred; 301 int peer_pidfd; 302 pid_t peer_pid; 303 int on = 0; 304 305 cfd = socket(AF_UNIX, variant->type, 0); 306 if (cfd < 0) { 307 log_err("socket"); 308 child_die(); 309 } 310 311 if (variant->type == SOCK_DGRAM) { 312 fill_sockaddr(self->client_addr, variant->abstract); 313 314 if (bind(cfd, (struct sockaddr *)&self->client_addr->listen_addr, self->client_addr->addrlen)) { 315 log_err("bind"); 316 child_die(); 317 } 318 } 319 320 if (connect(cfd, (struct sockaddr *)&self->server_addr.listen_addr, 321 self->server_addr.addrlen) != 0) { 322 log_err("connect"); 323 child_die(); 324 } 325 326 on = 1; 327 if (setsockopt(cfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { 328 log_err("Failed to set SO_PASSCRED"); 329 child_die(); 330 } 331 332 if (setsockopt(cfd, SOL_SOCKET, SO_PASSPIDFD, &on, sizeof(on))) { 333 log_err("Failed to set SO_PASSPIDFD"); 334 child_die(); 335 } 336 337 close(self->startup_pipe[1]); 338 339 if (cmsg_check(cfd)) { 340 log_err("cmsg_check failed"); 341 child_die(); 342 } 343 344 /* skip further for SOCK_DGRAM as it's not applicable */ 345 if (variant->type == SOCK_DGRAM) 346 return; 347 348 len = sizeof(peer_cred); 349 if (getsockopt(cfd, SOL_SOCKET, SO_PEERCRED, &peer_cred, &len)) { 350 log_err("Failed to get SO_PEERCRED"); 351 child_die(); 352 } 353 354 len = sizeof(peer_pidfd); 355 if (getsockopt(cfd, SOL_SOCKET, SO_PEERPIDFD, &peer_pidfd, &len)) { 356 log_err("Failed to get SO_PEERPIDFD"); 357 child_die(); 358 } 359 360 /* pid from SO_PEERCRED should point to the parent process PID */ 361 if (peer_cred.pid != getppid()) { 362 log_err("peer_cred.pid != getppid(): %d != %d", peer_cred.pid, getppid()); 363 child_die(); 364 } 365 366 peer_pid = get_pid_from_fdinfo_file(peer_pidfd, 367 "Pid:", sizeof("Pid:") - 1); 368 if (peer_pid != peer_cred.pid) { 369 log_err("peer_pid != peer_cred.pid: %d != %d", peer_pid, peer_cred.pid); 370 child_die(); 371 } 372 } 373 374 TEST_F(scm_pidfd, test) 375 { 376 int err; 377 int pfd; 378 int child_status = 0; 379 380 self->server = socket(AF_UNIX, variant->type, 0); 381 ASSERT_NE(-1, self->server); 382 383 fill_sockaddr(&self->server_addr, variant->abstract); 384 385 err = bind(self->server, (struct sockaddr *)&self->server_addr.listen_addr, self->server_addr.addrlen); 386 ASSERT_EQ(0, err); 387 388 if (variant->type == SOCK_STREAM) { 389 err = listen(self->server, 1); 390 ASSERT_EQ(0, err); 391 } 392 393 err = pipe(self->startup_pipe); 394 ASSERT_NE(-1, err); 395 396 self->client_pid = fork(); 397 ASSERT_NE(-1, self->client_pid); 398 if (self->client_pid == 0) { 399 close(self->server); 400 close(self->startup_pipe[0]); 401 client(self, variant); 402 exit(0); 403 } 404 close(self->startup_pipe[1]); 405 406 if (variant->type == SOCK_STREAM) { 407 pfd = accept(self->server, NULL, NULL); 408 ASSERT_NE(-1, pfd); 409 } else { 410 pfd = self->server; 411 } 412 413 /* wait until the child arrives at checkpoint */ 414 read(self->startup_pipe[0], &err, sizeof(int)); 415 close(self->startup_pipe[0]); 416 417 if (variant->type == SOCK_DGRAM) { 418 err = sendto(pfd, "x", sizeof(char), 0, (struct sockaddr *)&self->client_addr->listen_addr, self->client_addr->addrlen); 419 ASSERT_NE(-1, err); 420 } else { 421 err = send(pfd, "x", sizeof(char), 0); 422 ASSERT_NE(-1, err); 423 } 424 425 close(pfd); 426 waitpid(self->client_pid, &child_status, 0); 427 ASSERT_EQ(0, WIFEXITED(child_status) ? WEXITSTATUS(child_status) : 1); 428 } 429 430 TEST_HARNESS_MAIN 431