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