scm_pidfd.c 11 KB

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