• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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