xref: /xnu-8796.101.5/tests/test_knote_use_after_free.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 /*
2  * Test based on POC attached to rdar://96567281 (Knote Use-after-Free in XNU)
3  *
4  */
5 #include <darwintest.h>
6 #include <mach/mach.h>
7 #include <pthread.h>
8 #include <sys/event.h>
9 #include <stdlib.h>
10 
11 T_GLOBAL_META(
12 	T_META_NAMESPACE("xnu.ipc"),
13 	T_META_RADAR_COMPONENT_NAME("xnu"),
14 	T_META_RADAR_COMPONENT_VERSION("IPC"),
15 	T_META_RUN_CONCURRENTLY(TRUE));
16 
17 typedef struct knote_context_s knote_context_t;
18 struct knote_context_s {
19 	volatile int initialized;
20 	volatile int start;
21 };
22 
23 static void *
th_allocate_knotes(void * arg)24 th_allocate_knotes(void *arg)
25 {
26 	knote_context_t *context = (knote_context_t *)arg;
27 	kern_return_t kr = KERN_SUCCESS;
28 	T_QUIET; T_ASSERT_EQ(context->initialized, (int)0, "th_allocate_knotes context is initialized.");
29 
30 	mach_port_t sync_port = MACH_PORT_NULL;
31 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sync_port);
32 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_allocate sync_port");
33 
34 	mach_port_t kq_port = MACH_PORT_NULL;
35 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &kq_port);
36 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_allocate kq_port");
37 
38 	int kq = kqueue();
39 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kq, "kqueue");
40 
41 #define PORTS_COUNT 0x1000
42 
43 	mach_port_t *ports = calloc(PORTS_COUNT, sizeof(mach_port_t));
44 	T_QUIET; T_ASSERT_NOTNULL(ports, "calloc");
45 
46 	for (size_t i = 0; i < PORTS_COUNT; i++) {
47 		mach_port_t port = MACH_PORT_NULL;
48 		kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
49 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_allocate");
50 
51 		typedef struct move_receive_request_s move_receive_request_t;
52 		typedef struct move_receive_reply_s move_receive_reply_t;
53 
54 		struct move_receive_request_s {
55 			mach_msg_header_t header;
56 			mach_msg_body_t body;
57 			mach_msg_port_descriptor_t port;
58 			mach_msg_port_descriptor_t kq_port;
59 		};
60 
61 		struct move_receive_reply_s {
62 			mach_msg_header_t header;
63 			mach_msg_body_t body;
64 			mach_msg_port_descriptor_t port;
65 			mach_msg_port_descriptor_t kq_port;
66 			mach_msg_trailer_t trailer;
67 		};
68 
69 		union {
70 			move_receive_request_t request;
71 			move_receive_reply_t reply;
72 		} message;
73 
74 		move_receive_request_t *request = &message.request;
75 		move_receive_reply_t *reply = &message.reply;
76 
77 		request->header = (mach_msg_header_t){
78 			.msgh_remote_port = sync_port,
79 			.msgh_local_port = MACH_PORT_NULL,
80 			.msgh_voucher_port = MACH_PORT_NULL,
81 			.msgh_id = (mach_msg_id_t)0x88888888,
82 			.msgh_size = sizeof(*request),
83 			.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX),
84 		};
85 
86 		request->body = (mach_msg_body_t){
87 			.msgh_descriptor_count = 2,
88 		};
89 
90 		request->port = (mach_msg_port_descriptor_t){
91 			.name = port,
92 			.disposition = MACH_MSG_TYPE_MOVE_RECEIVE,
93 			.type = MACH_MSG_PORT_DESCRIPTOR,
94 		};
95 		request->kq_port = (mach_msg_port_descriptor_t){
96 			.name = kq_port,
97 			.disposition = MACH_MSG_TYPE_MOVE_RECEIVE,
98 			.type = MACH_MSG_PORT_DESCRIPTOR,
99 		};
100 
101 		kr = mach_msg(&request->header, MACH_SEND_MSG, sizeof(*request), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
102 		T_QUIET; T_ASSERT_EQ(kr, MACH_MSG_SUCCESS, "mach_msg");
103 
104 		struct kevent_qos_s event = {
105 			.ident = sync_port,
106 			.filter = EVFILT_MACHPORT,
107 			.flags = EV_ADD | EV_ENABLE | EV_DISPATCH,
108 			.qos = 0xA00,
109 			.udata = 0x42424242,
110 			.fflags = MACH_RCV_MSG,
111 			.xflags = 0x00,
112 			.data = 0x00,
113 			.ext = {(uint64_t)reply, sizeof(*reply), 0, 0},
114 		};
115 
116 		struct kevent_qos_s out_events[1];
117 
118 		int nevents = kevent_qos(kq, &event, 1, out_events, 1, NULL, NULL, 0);
119 		T_QUIET; T_ASSERT_EQ(nevents, (int)1, "kevent_qos");
120 		T_QUIET; T_ASSERT_EQ(out_events[0].udata, (uint64_t)0x42424242, "kevent_qos");
121 		T_QUIET; T_ASSERT_BITS_SET(reply->header.msgh_bits, MACH_MSGH_BITS_COMPLEX, "message is complex");
122 		T_QUIET; T_ASSERT_EQ(reply->body.msgh_descriptor_count, (mach_msg_size_t)2, "mach_msg");
123 
124 		ports[i] = reply->port.name;
125 		kq_port = reply->kq_port.name;
126 	}
127 
128 	context->initialized = 1;
129 	while (!context->start) {
130 	}
131 
132 	for (size_t i = 0; i < PORTS_COUNT; i++) {
133 		uint32_t wl_id = (uint32_t)0x99999999;
134 
135 		struct kevent_qos_s event = {
136 			.ident = ports[i],
137 			.filter = EVFILT_WORKLOOP,
138 			.flags = EV_ADD | EV_DISABLE,
139 			.qos = 0x00,
140 			.udata = 0x88888888,
141 			.fflags = NOTE_WL_SYNC_IPC,
142 			.xflags = 0x00,
143 			.data = 0x66666666,
144 			.ext = {0x00, 0x00, 0x00, 0x00},
145 		};
146 		struct kevent_qos_s output = { };
147 		int ret = kevent_id(wl_id, &event, 1, &output, 1, NULL, NULL,
148 		    KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS);
149 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kevent_id");
150 	}
151 	return NULL;
152 }
153 
154 T_DECL(test_knote_use_after_free,
155     "Verify knote use-after-free issue does not reproduce - rdar://96567281 (Knote Use-after-Free in XNU)",
156     T_META_CHECK_LEAKS(false))
157 {
158 	mach_port_t task = mach_task_self();
159 
160 	knote_context_t context = {
161 		.initialized = 0,
162 		.start = 0,
163 	};
164 
165 	pthread_t thknote;
166 	T_ASSERT_POSIX_ZERO(pthread_create(&thknote, NULL, th_allocate_knotes, &context), "pthread_create");
167 
168 	int kq = kqueue();
169 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kq, "kqueue");
170 
171 #define KNOTE_PORT_COUNT 2
172 
173 	kern_return_t kr = KERN_SUCCESS;
174 	mach_port_t sync_port = MACH_PORT_NULL, knote_port[KNOTE_PORT_COUNT];
175 	kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &sync_port);
176 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_allocate sync_port");
177 
178 	for (size_t i = 0; i < KNOTE_PORT_COUNT; i++) {
179 		knote_port[i] = MACH_PORT_NULL;
180 		kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &knote_port[i]);
181 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_allocate knote_port");
182 	}
183 
184 	typedef struct sync_knote_msg_local_s sync_knote_msg_local_t;
185 	typedef struct sync_knote_msg_remote_s sync_knote_msg_remote_t;
186 
187 #pragma pack(4)
188 	struct sync_knote_msg_local_s {
189 		mach_msg_header_t header;
190 		mach_msg_body_t body;
191 		mach_msg_port_descriptor_t port[KNOTE_PORT_COUNT];
192 		uint64_t sequence;
193 	};
194 #pragma pack(0)
195 
196 #pragma pack(4)
197 	struct sync_knote_msg_remote_s {
198 		mach_msg_header_t header;
199 		mach_msg_body_t body;
200 		mach_msg_port_descriptor_t port[KNOTE_PORT_COUNT];
201 		uint64_t sequence;
202 		mach_msg_trailer_t trailer;
203 	};
204 #pragma pack(0)
205 
206 	union {
207 		sync_knote_msg_local_t local;
208 		sync_knote_msg_remote_t remote;
209 	} message;
210 
211 	sync_knote_msg_local_t *local = &message.local;
212 	sync_knote_msg_remote_t *remote = &message.remote;
213 
214 	local->header = (mach_msg_header_t){
215 		.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX),
216 		.msgh_remote_port = sync_port,
217 		.msgh_local_port = MACH_PORT_NULL,
218 		.msgh_voucher_port = MACH_PORT_NULL,
219 		.msgh_size = sizeof(sync_knote_msg_local_t),
220 		.msgh_id =  (mach_msg_id_t)0x88888888,
221 	};
222 	local->body.msgh_descriptor_count = KNOTE_PORT_COUNT;
223 	for (size_t i = 0; i < KNOTE_PORT_COUNT; i++) {
224 		local->port[i] = (mach_msg_port_descriptor_t){
225 			.name = knote_port[i],
226 			.disposition = MACH_MSG_TYPE_MOVE_RECEIVE,
227 			.type = MACH_MSG_PORT_DESCRIPTOR,
228 		};
229 	}
230 	local->sequence = 0x6666666666666666;
231 	kr = mach_msg(&local->header, MACH_SEND_MSG, sizeof(sync_knote_msg_local_t), 0, MACH_PORT_NULL,
232 	    0, MACH_PORT_NULL);
233 	T_QUIET; T_ASSERT_EQ(kr, MACH_MSG_SUCCESS, "mach_msg");
234 
235 	struct kevent_qos_s event = {
236 		.ident = sync_port,
237 		.filter = EVFILT_MACHPORT,
238 		.flags = EV_ADD | EV_ENABLE | EV_DISPATCH,
239 		.qos = 0xA00,
240 		.udata = 42424242,
241 		.fflags = MACH_RCV_MSG,
242 		.xflags = 0x00,
243 		.data = 0x00,
244 		.ext = {(uint64_t)remote, sizeof(*remote), 0, 0},
245 	};
246 
247 	struct kevent_qos_s out_events[1];
248 
249 	int nevents = kevent_qos(kq, &event, 1, out_events, 1, NULL, NULL, 0);
250 	T_QUIET; T_ASSERT_EQ(nevents, (int)1, "kevent_qos nevents");
251 	T_QUIET; T_ASSERT_EQ(remote->sequence, (uint64_t)0x6666666666666666, "kevent_qos remote->sequence");
252 
253 	int ret = 0;
254 	struct kevent_qos_s del_event = {
255 		.ident = sync_port,
256 		.filter = EVFILT_MACHPORT,
257 		.flags = EV_DELETE,
258 		.qos = 0xA00,
259 		.udata = 0x00,
260 		.fflags = MACH_RCV_MSG,
261 		.xflags = 0x00,
262 		.data = 0x00,
263 		.ext = {0, 0, 0, 0},
264 	};
265 
266 	ret = kevent_qos(kq, &del_event, 1, NULL, 0, NULL, NULL, 0);
267 	T_QUIET; T_ASSERT_EQ(ret, (int)0, "kevent_qos return");
268 
269 	while (!context.initialized) {
270 	}
271 
272 	context.start = 1;
273 	T_ASSERT_POSIX_ZERO(pthread_join(thknote, NULL), "pthread_join");
274 
275 	kr = _kernelrpc_mach_port_insert_right_trap(task, sync_port, sync_port, MACH_MSG_TYPE_MOVE_RECEIVE);
276 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "_kernelrpc_mach_port_insert_right_trap");
277 }
278