1 /*
2 * repro-CVE-2020-29373 -- Reproducer for CVE-2020-29373.
3 *
4 * Copyright (c) 2021 SUSE
5 * Author: Nicolai Stange <nstange@suse.de>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <sys/mman.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <inttypes.h>
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include "liburing.h"
33
34 /*
35 * This attempts to make the kernel issue a sendmsg() to
36 * path from io_uring's async io_sq_wq_submit_work().
37 *
38 * Unfortunately, IOSQE_ASYNC is available only from kernel version
39 * 5.6 onwards. To still force io_uring to process the request
40 * asynchronously from io_sq_wq_submit_work(), queue a couple of
41 * auxiliary requests all failing with EAGAIN before. This is
42 * implemented by writing repeatedly to an auxiliary O_NONBLOCK
43 * AF_UNIX socketpair with a small SO_SNDBUF.
44 */
try_sendmsg_async(const char * const path)45 static int try_sendmsg_async(const char * const path)
46 {
47 int snd_sock, r;
48 struct io_uring ring;
49 char sbuf[16] = {};
50 struct iovec siov = { .iov_base = &sbuf, .iov_len = sizeof(sbuf) };
51 struct sockaddr_un addr = {};
52 struct msghdr msg = {
53 .msg_name = &addr,
54 .msg_namelen = sizeof(addr),
55 .msg_iov = &siov,
56 .msg_iovlen = 1,
57 };
58 struct io_uring_cqe *cqe;
59 struct io_uring_sqe *sqe;
60
61 snd_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
62 if (snd_sock < 0) {
63 perror("socket(AF_UNIX)");
64 return -1;
65 }
66
67 addr.sun_family = AF_UNIX;
68 strcpy(addr.sun_path, path);
69
70 r = io_uring_queue_init(512, &ring, 0);
71 if (r < 0) {
72 fprintf(stderr, "ring setup failed: %d\n", r);
73 goto close_iour;
74 }
75
76 sqe = io_uring_get_sqe(&ring);
77 if (!sqe) {
78 fprintf(stderr, "get sqe failed\n");
79 r = -EFAULT;
80 goto close_iour;
81 }
82
83 /* the actual one supposed to fail with -ENOENT. */
84 io_uring_prep_sendmsg(sqe, snd_sock, &msg, 0);
85 sqe->flags = IOSQE_ASYNC;
86 sqe->user_data = 255;
87
88 r = io_uring_submit(&ring);
89 if (r != 1) {
90 fprintf(stderr, "sqe submit failed: %d\n", r);
91 r = -EFAULT;
92 goto close_iour;
93 }
94
95 r = io_uring_wait_cqe(&ring, &cqe);
96 if (r < 0) {
97 fprintf(stderr, "wait completion %d\n", r);
98 r = -EFAULT;
99 goto close_iour;
100 }
101 if (cqe->user_data != 255) {
102 fprintf(stderr, "user data %d\n", r);
103 r = -EFAULT;
104 goto close_iour;
105 }
106 if (cqe->res != -ENOENT) {
107 r = 3;
108 fprintf(stderr,
109 "error: cqe %i: res=%i, but expected -ENOENT\n",
110 (int)cqe->user_data, (int)cqe->res);
111 }
112 io_uring_cqe_seen(&ring, cqe);
113
114 close_iour:
115 io_uring_queue_exit(&ring);
116 close(snd_sock);
117 return r;
118 }
119
main(int argc,char * argv[])120 int main(int argc, char *argv[])
121 {
122 int r;
123 char tmpdir[] = "/tmp/tmp.XXXXXX";
124 int rcv_sock;
125 struct sockaddr_un addr = {};
126 pid_t c;
127 int wstatus;
128
129 if (!mkdtemp(tmpdir)) {
130 perror("mkdtemp()");
131 return 1;
132 }
133
134 rcv_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
135 if (rcv_sock < 0) {
136 perror("socket(AF_UNIX)");
137 r = 1;
138 goto rmtmpdir;
139 }
140
141 addr.sun_family = AF_UNIX;
142 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/sock", tmpdir);
143
144 r = bind(rcv_sock, (struct sockaddr *)&addr,
145 sizeof(addr));
146 if (r < 0) {
147 perror("bind()");
148 close(rcv_sock);
149 r = 1;
150 goto rmtmpdir;
151 }
152
153 c = fork();
154 if (!c) {
155 close(rcv_sock);
156
157 if (chroot(tmpdir)) {
158 perror("chroot()");
159 return 1;
160 }
161
162 r = try_sendmsg_async(addr.sun_path);
163 if (r < 0) {
164 /* system call failure */
165 r = 1;
166 } else if (r) {
167 /* test case failure */
168 r += 1;
169 }
170 return r;
171 }
172
173 if (waitpid(c, &wstatus, 0) == (pid_t)-1) {
174 perror("waitpid()");
175 r = 1;
176 goto rmsock;
177 }
178 if (!WIFEXITED(wstatus)) {
179 fprintf(stderr, "child got terminated\n");
180 r = 1;
181 goto rmsock;
182 }
183 r = WEXITSTATUS(wstatus);
184 if (r)
185 fprintf(stderr, "error: Test failed\n");
186 rmsock:
187 close(rcv_sock);
188 unlink(addr.sun_path);
189 rmtmpdir:
190 rmdir(tmpdir);
191 return r;
192 }
193