1 /*
2  * Copyright © 2019 Manuel Stoeckl
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "parsing.h"
27 #include "main.h"
28 #include "util.h"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <symgen_types.h>
34 
get_type_name(struct wp_object * obj)35 static const char *get_type_name(struct wp_object *obj)
36 {
37 	return obj->type ? obj->type->name : "<no type>";
38 }
get_nth_packed_string(const char * pack,int n)39 const char *get_nth_packed_string(const char *pack, int n)
40 {
41 	for (int i = 0; i < n; i++) {
42 		pack += strlen(pack) + 1;
43 	}
44 	return pack;
45 }
46 
tree_rotate_left(struct wp_object * n)47 static struct wp_object *tree_rotate_left(struct wp_object *n)
48 {
49 	struct wp_object *tmp = n->t_right;
50 	n->t_right = tmp->t_left;
51 	tmp->t_left = n;
52 	return tmp;
53 }
tree_rotate_right(struct wp_object * n)54 static struct wp_object *tree_rotate_right(struct wp_object *n)
55 {
56 	struct wp_object *tmp = n->t_left;
57 	n->t_left = tmp->t_right;
58 	tmp->t_right = n;
59 	return tmp;
60 }
tree_link_right(struct wp_object ** cur,struct wp_object ** rn)61 static void tree_link_right(struct wp_object **cur, struct wp_object **rn)
62 {
63 	(*rn)->t_left = *cur;
64 	*rn = *cur;
65 	*cur = (*cur)->t_left;
66 }
tree_link_left(struct wp_object ** cur,struct wp_object ** ln)67 static void tree_link_left(struct wp_object **cur, struct wp_object **ln)
68 {
69 	(*ln)->t_right = *cur;
70 	*ln = *cur;
71 	*cur = (*cur)->t_right;
72 }
73 
74 /* Splay operation, following Sleator+Tarjan, 1985 */
tree_branch_splay(struct wp_object * root,uint32_t key)75 static struct wp_object *tree_branch_splay(struct wp_object *root, uint32_t key)
76 {
77 	if (!root) {
78 		return NULL;
79 	}
80 	struct wp_object bg = {.t_left = NULL, .t_right = NULL};
81 	struct wp_object *ln = &bg;
82 	struct wp_object *rn = &bg;
83 	struct wp_object *cur = root;
84 
85 	while (key != cur->obj_id) {
86 		if (key < cur->obj_id) {
87 			if (cur->t_left && key < cur->t_left->obj_id) {
88 				cur = tree_rotate_right(cur);
89 			}
90 			if (!cur->t_left) {
91 				break;
92 			}
93 			tree_link_right(&cur, &rn);
94 		} else {
95 			if (cur->t_right && key > cur->t_right->obj_id) {
96 				cur = tree_rotate_left(cur);
97 			}
98 			if (!cur->t_right) {
99 				break;
100 			}
101 			tree_link_left(&cur, &ln);
102 		}
103 	}
104 	ln->t_right = cur->t_left;
105 	rn->t_left = cur->t_right;
106 	cur->t_left = bg.t_right;
107 	cur->t_right = bg.t_left;
108 	return cur;
109 }
tree_insert(struct wp_object ** tree,struct wp_object * new_node)110 static void tree_insert(struct wp_object **tree, struct wp_object *new_node)
111 {
112 	/* Reset these, just in case */
113 	new_node->t_left = NULL;
114 	new_node->t_right = NULL;
115 
116 	struct wp_object *r = *tree;
117 	if (!r) {
118 		*tree = new_node;
119 		return;
120 	}
121 	r = tree_branch_splay(r, new_node->obj_id);
122 	if (new_node->obj_id < r->obj_id) {
123 		new_node->t_left = r->t_left;
124 		new_node->t_right = r;
125 		r->t_left = NULL;
126 		r = new_node;
127 	} else if (new_node->obj_id > r->obj_id) {
128 		new_node->t_right = r->t_right;
129 		new_node->t_left = r;
130 		r->t_right = NULL;
131 		r = new_node;
132 	} else {
133 		/* already in tree, no effect? or do silent override */
134 	}
135 	*tree = r;
136 }
tree_remove(struct wp_object ** tree,uint32_t key)137 static void tree_remove(struct wp_object **tree, uint32_t key)
138 {
139 	struct wp_object *r = *tree;
140 	r = tree_branch_splay(r, key);
141 	if (!r || r->obj_id != key) {
142 		/* wasn't in tree */
143 		return;
144 	}
145 	struct wp_object *lbranch = r->t_left;
146 	struct wp_object *rbranch = r->t_right;
147 	if (!lbranch) {
148 		*tree = rbranch;
149 		return;
150 	}
151 	r = tree_branch_splay(lbranch, key);
152 	r->t_right = rbranch;
153 	*tree = r;
154 }
tree_lookup(struct wp_object ** tree,uint32_t key)155 static struct wp_object *tree_lookup(struct wp_object **tree, uint32_t key)
156 {
157 	*tree = tree_branch_splay(*tree, key);
158 	if (*tree && (*tree)->obj_id == key) {
159 		return *tree;
160 	}
161 	return NULL;
162 }
tree_clear(struct wp_object ** tree,void (* node_free)(struct wp_object * object))163 static void tree_clear(struct wp_object **tree,
164 		void (*node_free)(struct wp_object *object))
165 {
166 	struct wp_object *root = *tree;
167 	while (root) {
168 		root = tree_branch_splay(root, 0);
169 		struct wp_object *right = root->t_right;
170 		root->t_right = NULL;
171 		node_free(root);
172 		root = right;
173 	}
174 	*tree = NULL;
175 }
176 
tracker_insert(struct message_tracker * mt,struct wp_object * obj)177 void tracker_insert(struct message_tracker *mt, struct wp_object *obj)
178 {
179 	struct wp_object *old_obj = tree_lookup(&mt->objtree_root, obj->obj_id);
180 	if (old_obj) {
181 		/* We /always/ replace the object, to ensure that map
182 		 * elements are never duplicated and make the deletion
183 		 * process cause crashes */
184 		if (!old_obj->is_zombie) {
185 			wp_error("Replacing object @%u that already exists: old type %s, new type %s",
186 					obj->obj_id, get_type_name(old_obj),
187 					get_type_name(obj));
188 		}
189 		/* Zombie objects (server allocated, client deleted) are
190 		 * only acknowledged destroyed by the server when they
191 		 * are replaced. */
192 		tree_remove(&mt->objtree_root, old_obj->obj_id);
193 		destroy_wp_object(old_obj);
194 	}
195 
196 	tree_insert(&mt->objtree_root, obj);
197 }
tracker_replace_existing(struct message_tracker * mt,struct wp_object * new_obj)198 void tracker_replace_existing(
199 		struct message_tracker *mt, struct wp_object *new_obj)
200 {
201 	tree_remove(&mt->objtree_root, new_obj->obj_id);
202 	tree_insert(&mt->objtree_root, new_obj);
203 }
tracker_remove(struct message_tracker * mt,struct wp_object * obj)204 void tracker_remove(struct message_tracker *mt, struct wp_object *obj)
205 {
206 	tree_remove(&mt->objtree_root, obj->obj_id);
207 }
tracker_get(struct message_tracker * mt,uint32_t id)208 struct wp_object *tracker_get(struct message_tracker *mt, uint32_t id)
209 {
210 	return tree_lookup(&mt->objtree_root, id);
211 }
get_object(struct message_tracker * mt,uint32_t id,const struct wp_interface * intf)212 struct wp_object *get_object(struct message_tracker *mt, uint32_t id,
213 		const struct wp_interface *intf)
214 {
215 	(void)intf;
216 	return tracker_get(mt, id);
217 }
218 
init_message_tracker(struct message_tracker * mt)219 int init_message_tracker(struct message_tracker *mt)
220 {
221 	memset(mt, 0, sizeof(*mt));
222 
223 	/* heap allocate this, so we don't need to protect against adversarial
224 	 * replacement */
225 	struct wp_object *disp = create_wp_object(1, the_display_interface);
226 	if (!disp) {
227 		return -1;
228 	}
229 	tracker_insert(mt, disp);
230 	return 0;
231 }
cleanup_message_tracker(struct message_tracker * mt)232 void cleanup_message_tracker(struct message_tracker *mt)
233 {
234 	tree_clear(&mt->objtree_root, destroy_wp_object);
235 }
236 
word_has_empty_bytes(uint32_t v)237 static bool word_has_empty_bytes(uint32_t v)
238 {
239 	return ((v & 0xFF) == 0) || ((v & 0xFF00) == 0) ||
240 	       ((v & 0xFF0000) == 0) || ((v & 0xFF000000) == 0);
241 }
242 
size_check(const struct msg_data * data,const uint32_t * payload,unsigned int true_length,int fd_length)243 bool size_check(const struct msg_data *data, const uint32_t *payload,
244 		unsigned int true_length, int fd_length)
245 {
246 	if (data->n_fds > fd_length) {
247 		wp_error("Msg overflow, not enough fds %d > %d", data->n_fds,
248 				fd_length);
249 		return false;
250 	}
251 
252 	const uint16_t *gaps = data->gaps;
253 	uint32_t pos = 0;
254 	for (;; gaps++) {
255 		uint16_t g = (*gaps >> 2);
256 		uint16_t e = (*gaps & 0x3);
257 		pos += g;
258 		if (pos > true_length) {
259 			wp_error("Msg overflow, not enough words %d > %d", pos,
260 					true_length);
261 			return false;
262 		}
263 		switch (e) {
264 		case GAP_CODE_STR: {
265 			uint32_t x_words = (payload[pos - 1] + 3) / 4;
266 			uint32_t end_idx = pos + x_words - 1;
267 			if (end_idx < true_length &&
268 					!word_has_empty_bytes(
269 							payload[end_idx])) {
270 				wp_error("Msg overflow, string termination %d < %d, %d, %x %d",
271 						pos, true_length, x_words,
272 						payload[end_idx],
273 						word_has_empty_bytes(
274 								payload[end_idx]));
275 				return false;
276 			}
277 			pos += x_words;
278 		} break;
279 		case GAP_CODE_ARR:
280 			pos += (payload[pos - 1] + 3) / 4;
281 			break;
282 		case GAP_CODE_OBJ:
283 			break;
284 		case GAP_CODE_END:
285 			return true;
286 		}
287 	}
288 }
289 
290 /* Given a size-checked request, try to construct all the new objects
291  * that the request requires. Return true if successful, false otherwise.
292  *
293  * The argument `caller_obj` should be the object on which the request was
294  * invoked; this function checks to make sure that object is not
295  * overwritten by accident/corrupt input.
296  */
build_new_objects(const struct msg_data * data,const uint32_t * payload,struct message_tracker * mt,const struct wp_object * caller_obj,int msg_offset)297 static bool build_new_objects(const struct msg_data *data,
298 		const uint32_t *payload, struct message_tracker *mt,
299 		const struct wp_object *caller_obj, int msg_offset)
300 {
301 	const uint16_t *gaps = data->gaps;
302 	uint32_t pos = 0;
303 	uint32_t objno = 0;
304 	for (;; gaps++) {
305 		uint16_t g = (*gaps >> 2);
306 		uint16_t e = (*gaps & 0x3);
307 		pos += g;
308 		switch (e) {
309 		case GAP_CODE_STR:
310 		case GAP_CODE_ARR:
311 			pos += (payload[pos - 1] + 3) / 4;
312 			break;
313 		case GAP_CODE_OBJ: {
314 			uint32_t new_id = payload[pos - 1];
315 			if (new_id == caller_obj->obj_id) {
316 				wp_error("In %s.%s, tried to create object id=%u conflicting with object being called, also id=%u",
317 						caller_obj->type->name,
318 						get_nth_packed_string(
319 								caller_obj->type->msg_names,
320 								msg_offset),
321 						new_id, caller_obj->obj_id);
322 				return false;
323 			}
324 			struct wp_object *new_obj = create_wp_object(
325 					new_id, data->new_objs[objno]);
326 			if (!new_obj) {
327 				return false;
328 			}
329 			tracker_insert(mt, new_obj);
330 			objno++;
331 		} break;
332 		case GAP_CODE_END:
333 			return true;
334 		}
335 	}
336 }
337 
peek_message_size(const void * data)338 int peek_message_size(const void *data)
339 {
340 	return (int)(((const uint32_t *)data)[1] >> 16);
341 }
342 
handle_message(struct globals * g,bool display_side,bool from_client,struct char_window * chars,struct int_window * fds)343 enum parse_state handle_message(struct globals *g, bool display_side,
344 		bool from_client, struct char_window *chars,
345 		struct int_window *fds)
346 {
347 	bool to_wire = from_client == !display_side;
348 
349 	const uint32_t *const header =
350 			(uint32_t *)&chars->data[chars->zone_start];
351 	uint32_t obj = header[0];
352 	int len = (int)(header[1] >> 16);
353 	int meth = (int)((header[1] << 16) >> 16);
354 
355 	if (len != chars->zone_end - chars->zone_start) {
356 		wp_error("Message length disagreement %d vs %d", len,
357 				chars->zone_end - chars->zone_start);
358 		return PARSE_ERROR;
359 	}
360 	// display: object = 0?
361 	struct wp_object *objh = tracker_get(&g->tracker, obj);
362 	if (!objh || !objh->type) {
363 		wp_debug("Unidentified object %d with %s", obj,
364 				from_client ? "request" : "event");
365 		return PARSE_UNKNOWN;
366 	}
367 
368 	/* Identify the message type. Messages sent over the wire are tagged
369 	 * with the number of file descriptors that are bound to the message.
370 	 * This incidentally limits the number of fds to 31, and number of
371 	 * messages per type 2047. */
372 	int num_fds_with_message = -1;
373 	if (!to_wire) {
374 		num_fds_with_message = meth >> 11;
375 		meth = meth & ((1 << 11) - 1);
376 		if (num_fds_with_message > 0) {
377 			wp_debug("Reading message tagged with %d fds.",
378 					num_fds_with_message);
379 		}
380 		// Strip out the FD counters
381 		((uint32_t *)&chars->data[chars->zone_start])[1] &=
382 				~(uint32_t)((1 << 16) - (1 << 11));
383 	}
384 
385 	const struct wp_interface *intf = objh->type;
386 	int nmsgs = from_client ? intf->nreq : intf->nevt;
387 	if (meth < 0 || meth >= nmsgs) {
388 		wp_debug("Unidentified request #%d (of %d) on interface %s",
389 				meth, nmsgs, intf->name);
390 		return PARSE_UNKNOWN;
391 	}
392 	int meth_offset = from_client ? meth : meth + intf->nreq;
393 	const struct msg_data *msg = &intf->msgs[meth_offset];
394 
395 	const uint32_t *payload = header + 2;
396 	if (!size_check(msg, payload, (unsigned int)len / 4 - 2,
397 			    fds->zone_end - fds->zone_start)) {
398 		wp_error("Message %x %s@%u.%s parse length overflow", payload,
399 				intf->name, objh->obj_id,
400 				get_nth_packed_string(
401 						intf->msg_names, meth_offset));
402 		return PARSE_UNKNOWN;
403 	}
404 
405 	if (!build_new_objects(msg, payload, &g->tracker, objh, meth_offset)) {
406 		return PARSE_UNKNOWN;
407 	}
408 
409 	int fds_used = 0;
410 	struct context ctx = {
411 			.g = g,
412 			.tracker = &g->tracker,
413 			.obj = objh,
414 			.on_display_side = display_side,
415 			.drop_this_msg = false,
416 			.message = (uint32_t *)&chars->data[chars->zone_start],
417 			.message_length = len,
418 			.message_available_space =
419 					chars->size - chars->zone_start,
420 			.fds = fds,
421 			.fds_changed = false,
422 	};
423 	if (msg->call) {
424 		(*msg->call)(&ctx, payload, &fds->data[fds->zone_start],
425 				&g->tracker);
426 	}
427 	if (num_fds_with_message >= 0 && msg->n_fds != num_fds_with_message) {
428 		wp_error("Message used %d file descriptors, but was tagged as using %d",
429 				msg->n_fds, num_fds_with_message);
430 	}
431 
432 	fds_used += msg->n_fds;
433 
434 	if (objh->obj_id >= 0xff000000 && msg->is_destructor) {
435 		/* Unfortunately, the wayland server library does not explicitly
436 		 * acknowledge the client requested deletion of objects that the
437 		 * wayland server has created; the client assumes success,
438 		 * except by creating a new object that overrides the existing
439 		 * id.
440 		 *
441 		 * To correctly vanish all events in flight, we mark the element
442 		 * as having been zombified; it will only be destroyed when a
443 		 * new element is created to take its place, since there could
444 		 * still be e.g. data transfers in the channel, and it's best
445 		 * that those only vanish when needed.
446 		 *
447 		 * Fortunately, wl_registry::bind objects are all client
448 		 * produced.
449 		 *
450 		 * TODO: early struct shadow_fd closure for all deletion
451 		 * requests, with a matching zombie flag to vanish transfers;
452 		 *
453 		 * TODO: avert the zombie apocalypse, where the compositor
454 		 * sends creation notices for a full hierarchy of objects
455 		 * before it receives the root's .destroy request.
456 		 */
457 		objh->is_zombie = true;
458 	}
459 
460 	if (ctx.drop_this_msg) {
461 		wp_debug("Dropping %s.%s, with %d fds", intf->name,
462 				get_nth_packed_string(
463 						intf->msg_names, meth_offset),
464 				fds_used);
465 		chars->zone_end = chars->zone_start;
466 		int nmoved = fds->zone_end - fds->zone_start - fds_used;
467 		memmove(&fds->data[fds->zone_start],
468 				&fds->data[fds->zone_start + fds_used],
469 				(size_t)nmoved * sizeof(int));
470 		fds->zone_end -= fds_used;
471 		return PARSE_KNOWN;
472 	}
473 
474 	if (!ctx.fds_changed) {
475 		// Default, autoadvance fd queue, unless handler disagreed.
476 		fds->zone_start += fds_used;
477 
478 		// Tag message with number of FDs. If the fds were modified
479 		// nontrivially, (i.e, ctx.fds_changed is true), tagging is
480 		// handler's responsibility
481 		if (to_wire) {
482 			if (fds_used >= 32 || meth >= 2048) {
483 				wp_error("Message used %d>=32 file descriptors or had index %d>=2048. FD tagging failed, expect a crash.",
484 						fds_used, meth);
485 			}
486 			if (fds_used > 0) {
487 				wp_debug("Tagging message with %d fds.",
488 						fds_used);
489 				((uint32_t *)&chars->data[chars->zone_start])
490 						[1] |=
491 						(uint32_t)(fds_used << 11);
492 			}
493 		}
494 	}
495 
496 	if (fds->zone_end < fds->zone_start) {
497 		wp_error("Handler error after %s.%s: fdzs = %d > %d = fdze",
498 				intf->name,
499 				get_nth_packed_string(
500 						intf->msg_names, meth_offset),
501 				fds->zone_start, fds->zone_end);
502 	}
503 	// Move the end, in case there were changes
504 	chars->zone_end = chars->zone_start + ctx.message_length;
505 	return PARSE_KNOWN;
506 }
507 
parse_and_prune_messages(struct globals * g,bool on_display_side,bool from_client,struct char_window * source_bytes,struct char_window * dest_bytes,struct int_window * fds)508 void parse_and_prune_messages(struct globals *g, bool on_display_side,
509 		bool from_client, struct char_window *source_bytes,
510 		struct char_window *dest_bytes, struct int_window *fds)
511 {
512 	bool anything_unknown = false;
513 	struct char_window scan_bytes;
514 	scan_bytes.data = dest_bytes->data;
515 	scan_bytes.zone_start = dest_bytes->zone_start;
516 	scan_bytes.zone_end = dest_bytes->zone_start;
517 	scan_bytes.size = dest_bytes->size;
518 
519 	DTRACE_PROBE1(waypipe, parse_enter,
520 			source_bytes->zone_end - source_bytes->zone_start);
521 
522 	for (; source_bytes->zone_start < source_bytes->zone_end;) {
523 		if (source_bytes->zone_end - source_bytes->zone_start < 8) {
524 			// Not enough remaining bytes to parse the
525 			// header
526 			wp_debug("Insufficient bytes for header: %d %d",
527 					source_bytes->zone_start,
528 					source_bytes->zone_end);
529 			break;
530 		}
531 		int msgsz = peek_message_size(
532 				&source_bytes->data[source_bytes->zone_start]);
533 		if (msgsz % 4 != 0) {
534 			wp_debug("Wayland messages lengths must be divisible by 4");
535 			break;
536 		}
537 		if (source_bytes->zone_start + msgsz > source_bytes->zone_end) {
538 			wp_debug("Insufficient bytes");
539 			// Not enough remaining bytes to contain the
540 			// message
541 			break;
542 		}
543 		if (msgsz < 8) {
544 			wp_debug("Degenerate message, claimed len=%d", msgsz);
545 			// Not enough remaining bytes to contain the
546 			// message
547 			break;
548 		}
549 
550 		/* We copy the message to the trailing end of the
551 		 * in-progress buffer; the parser may elect to modify
552 		 * the message's size */
553 		memcpy(&scan_bytes.data[scan_bytes.zone_start],
554 				&source_bytes->data[source_bytes->zone_start],
555 				(size_t)msgsz);
556 		source_bytes->zone_start += msgsz;
557 		scan_bytes.zone_end = scan_bytes.zone_start + msgsz;
558 
559 		enum parse_state pstate = handle_message(g, on_display_side,
560 				from_client, &scan_bytes, fds);
561 		if (pstate == PARSE_UNKNOWN || pstate == PARSE_ERROR) {
562 			anything_unknown = true;
563 		}
564 		scan_bytes.zone_start = scan_bytes.zone_end;
565 	}
566 	dest_bytes->zone_end = scan_bytes.zone_end;
567 
568 	if (anything_unknown) {
569 		// All-un-owned buffers are assumed to have changed.
570 		// (Note that in some cases, a new protocol could imply
571 		// a change for an existing buffer; it may make sense to
572 		// mark everything dirty, then.)
573 
574 		for (struct shadow_fd_link *lcur = g->map.link.l_next,
575 					   *lnxt = lcur->l_next;
576 				lcur != &g->map.link;
577 				lcur = lnxt, lnxt = lcur->l_next) {
578 			struct shadow_fd *cur = (struct shadow_fd *)lcur;
579 			if (!cur->has_owner) {
580 				cur->is_dirty = true;
581 			}
582 		}
583 	}
584 	DTRACE_PROBE(waypipe, parse_exit);
585 	return;
586 }
587