ring-leak.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Based on description from Al Viro - this demonstrates a leak of the
  5. * io_uring instance, by sending the io_uring fd over a UNIX socket.
  6. *
  7. * See:
  8. *
  9. * https://lore.kernel.org/linux-block/20190129192702.3605-1-axboe@kernel.dk/T/#m6c87fc64e4d063786af6ec6fadce3ac1e95d3184
  10. *
  11. */
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <stddef.h>
  15. #include <signal.h>
  16. #include <inttypes.h>
  17. #include <sys/types.h>
  18. #include <sys/syscall.h>
  19. #include <sys/socket.h>
  20. #include <sys/wait.h>
  21. #include <fcntl.h>
  22. #include <unistd.h>
  23. #include <string.h>
  24. #include <linux/fs.h>
  25. #include "liburing.h"
  26. #include "helpers.h"
  27. #include "../src/syscall.h"
  28. static int __io_uring_register_files(int ring_fd, int fd1, int fd2)
  29. {
  30. __s32 fds[2] = { fd1, fd2 };
  31. return __sys_io_uring_register(ring_fd, IORING_REGISTER_FILES, fds, 2);
  32. }
  33. static int get_ring_fd(void)
  34. {
  35. struct io_uring_params p;
  36. int fd;
  37. memset(&p, 0, sizeof(p));
  38. fd = __sys_io_uring_setup(2, &p);
  39. if (fd < 0) {
  40. perror("io_uring_setup");
  41. return -1;
  42. }
  43. return fd;
  44. }
  45. static int send_fd(int socket, int fd)
  46. {
  47. char buf[CMSG_SPACE(sizeof(fd))];
  48. struct cmsghdr *cmsg;
  49. struct msghdr msg;
  50. memset(buf, 0, sizeof(buf));
  51. memset(&msg, 0, sizeof(msg));
  52. msg.msg_control = buf;
  53. msg.msg_controllen = sizeof(buf);
  54. cmsg = CMSG_FIRSTHDR(&msg);
  55. cmsg->cmsg_level = SOL_SOCKET;
  56. cmsg->cmsg_type = SCM_RIGHTS;
  57. cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
  58. memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
  59. msg.msg_controllen = CMSG_SPACE(sizeof(fd));
  60. if (sendmsg(socket, &msg, 0) < 0) {
  61. if (errno == EINVAL)
  62. return T_EXIT_SKIP;
  63. perror("sendmsg");
  64. return T_EXIT_FAIL;
  65. }
  66. return T_EXIT_PASS;
  67. }
  68. static int test_iowq_request_cancel(void)
  69. {
  70. char buffer[128];
  71. struct io_uring ring;
  72. struct io_uring_sqe *sqe;
  73. int ret, fds[2];
  74. ret = io_uring_queue_init(8, &ring, 0);
  75. if (ret < 0) {
  76. fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
  77. return ret;
  78. }
  79. if (pipe(fds)) {
  80. perror("pipe");
  81. return -1;
  82. }
  83. ret = io_uring_register_files(&ring, fds, 2);
  84. if (ret) {
  85. fprintf(stderr, "file_register: %d\n", ret);
  86. return ret;
  87. }
  88. close(fds[1]);
  89. sqe = io_uring_get_sqe(&ring);
  90. if (!sqe) {
  91. fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
  92. return 1;
  93. }
  94. /* potentially sitting in internal polling */
  95. io_uring_prep_read(sqe, 0, buffer, 10, 0);
  96. sqe->flags |= IOSQE_FIXED_FILE;
  97. sqe = io_uring_get_sqe(&ring);
  98. if (!sqe) {
  99. fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
  100. return 1;
  101. }
  102. /* staying in io-wq */
  103. io_uring_prep_read(sqe, 0, buffer, 10, 0);
  104. sqe->flags |= IOSQE_FIXED_FILE | IOSQE_ASYNC;
  105. ret = io_uring_submit(&ring);
  106. if (ret != 2) {
  107. fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
  108. return 1;
  109. }
  110. /* should unregister files and close the write fd */
  111. io_uring_queue_exit(&ring);
  112. /*
  113. * We're trying to wait for the ring to "really" exit, that will be
  114. * done async. For that rely on the registered write end to be closed
  115. * after ring quiesce, so failing read from the other pipe end.
  116. */
  117. ret = read(fds[0], buffer, 10);
  118. if (ret < 0)
  119. perror("read");
  120. close(fds[0]);
  121. return 0;
  122. }
  123. static void trigger_unix_gc(void)
  124. {
  125. int fd;
  126. fd = socket(AF_UNIX, SOCK_DGRAM, 0);
  127. if (fd < 0)
  128. perror("socket dgram");
  129. else
  130. close(fd);
  131. }
  132. static int test_scm_cycles(bool update)
  133. {
  134. char buffer[128];
  135. struct io_uring ring;
  136. int i, ret;
  137. int sp[2], fds[2], reg_fds[4];
  138. if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) {
  139. perror("Failed to create Unix-domain socket pair\n");
  140. return 1;
  141. }
  142. ret = io_uring_queue_init(8, &ring, 0);
  143. if (ret < 0) {
  144. fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
  145. return ret;
  146. }
  147. if (pipe(fds)) {
  148. perror("pipe");
  149. return -1;
  150. }
  151. ret = send_fd(sp[0], ring.ring_fd);
  152. if (ret != T_EXIT_PASS)
  153. return ret;
  154. /* register an empty set for updates */
  155. if (update) {
  156. for (i = 0; i < 4; i++)
  157. reg_fds[i] = -1;
  158. ret = io_uring_register_files(&ring, reg_fds, 4);
  159. if (ret) {
  160. fprintf(stderr, "file_register: %d\n", ret);
  161. return ret;
  162. }
  163. }
  164. reg_fds[0] = fds[0];
  165. reg_fds[1] = fds[1];
  166. reg_fds[2] = sp[0];
  167. reg_fds[3] = sp[1];
  168. if (update) {
  169. ret = io_uring_register_files_update(&ring, 0, reg_fds, 4);
  170. if (ret != 4) {
  171. fprintf(stderr, "file_register: %d\n", ret);
  172. return ret;
  173. }
  174. } else {
  175. ret = io_uring_register_files(&ring, reg_fds, 4);
  176. if (ret) {
  177. fprintf(stderr, "file_register: %d\n", ret);
  178. return ret;
  179. }
  180. }
  181. close(fds[1]);
  182. close(sp[0]);
  183. close(sp[1]);
  184. /* should unregister files and close the write fd */
  185. io_uring_queue_exit(&ring);
  186. trigger_unix_gc();
  187. /*
  188. * We're trying to wait for the ring to "really" exit, that will be
  189. * done async. For that rely on the registered write end to be closed
  190. * after ring quiesce, so failing read from the other pipe end.
  191. */
  192. ret = read(fds[0], buffer, 10);
  193. if (ret < 0)
  194. perror("read");
  195. close(fds[0]);
  196. return 0;
  197. }
  198. int main(int argc, char *argv[])
  199. {
  200. int sp[2], pid, ring_fd, ret;
  201. int i;
  202. if (argc > 1)
  203. return 0;
  204. ret = test_iowq_request_cancel();
  205. if (ret) {
  206. fprintf(stderr, "test_iowq_request_cancel() failed\n");
  207. return 1;
  208. }
  209. for (i = 0; i < 2; i++) {
  210. bool update = !!(i & 1);
  211. ret = test_scm_cycles(update);
  212. if (ret == T_EXIT_SKIP)
  213. return T_EXIT_SKIP;
  214. if (ret) {
  215. fprintf(stderr, "test_scm_cycles() failed %i\n",
  216. update);
  217. return 1;
  218. }
  219. }
  220. if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) {
  221. perror("Failed to create Unix-domain socket pair\n");
  222. return 1;
  223. }
  224. ring_fd = get_ring_fd();
  225. if (ring_fd < 0)
  226. return 1;
  227. ret = __io_uring_register_files(ring_fd, sp[0], sp[1]);
  228. if (ret < 0) {
  229. perror("register files");
  230. return 1;
  231. }
  232. pid = fork();
  233. if (pid) {
  234. ret = send_fd(sp[0], ring_fd);
  235. if (ret != T_EXIT_PASS)
  236. return ret;
  237. }
  238. close(ring_fd);
  239. close(sp[0]);
  240. close(sp[1]);
  241. return 0;
  242. }