DF-3881 / mst_lct_model.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | /* DF-3881 model: unbounded MST topology depth (LCT) -> OOB writes. * * Faithful arithmetic model of the DragonFlyBSD kernel code paths in * sys/dev/drm/drm_dp_mst_topology.c: * * drm_dp_port_setup_pdt() line ~1074: u8 rad[6]; * drm_dp_calculate_rad() line ~1052: idx = (parent_lct - 1) / 2; * memcpy(rad, port->parent->rad, idx + 1); * rad[idx] |= port->port_num << shift; * return parent_lct + 1; * drm_dp_add_mst_branch_device() line ~840: memcpy(mstb->rad, rad, lct / 2); * struct drm_dp_mst_branch { u8 rad[8]; ... } * set_hdr_from_dst_qlock() line ~1484: memcpy(hdr->rad, mstb->rad, mstb->lct / 2); * struct drm_dp_sideband_msg_hdr { u8 rad[8]; ... } * drm_dp_mst_dump_mstb() line ~2954: int tabs = mstb->lct; char prefix[10]; * for (i = 0; i < tabs; i++) prefix[i] = '\t'; * * parent_lct grows by exactly +1 per nested MST branch device reported by the * hub in its LINK_ADDRESS reply (add_port -> port_setup_pdt -> +1 -> recurse * via drm_dp_send_link_address), and NOTHING in the file bounds it. * * This model reproduces the offset arithmetic (which kernel buffer index is * written out of bounds at which depth). It cannot drive the kernel path on * this guest because the guest has no DisplayPort MST hardware (no /dev/dri). */ #include <stdio.h> #include <string.h> #include <stdint.h> struct mstb_model { /* mirrors struct drm_dp_mst_branch */ uint8_t rad[8]; uint8_t lct; }; #define RAD_STACK 6 /* u8 rad[6] in drm_dp_port_setup_pdt */ #define RAD_HEAP 8 /* u8 rad[8] in struct drm_dp_mst_branch */ #define PREFIX 10 /* char prefix[10] in drm_dp_mst_dump_mstb */ int main(void) { struct mstb_model parent; int parent_lct; int first_stack_oob = -1, first_heap_oob = -1, first_hdr_oob = -1; int first_prefix_oob = -1; memset(&parent, 0, sizeof(parent)); parent.lct = 1; /* mgr->mst_primary is created with LCT 1 */ printf("modeling chain of nested MST branch devices (parent_lct = depth):\n"); for (parent_lct = 1; parent_lct <= 32; parent_lct++) { int idx = (parent_lct - 1) / 2; int shift = (parent_lct % 2) ? 4 : 0; int lct = parent_lct + 1; int stack_oob = 0, heap_oob = 0, hdr_oob = 0, prefix_oob = 0; /* drm_dp_calculate_rad() */ if (parent_lct > 1 && (idx + 1) > RAD_STACK) { stack_oob = 1; if (first_stack_oob < 0) first_stack_oob = parent_lct; } if (idx >= RAD_STACK) { stack_oob = 1; if (first_stack_oob < 0) first_stack_oob = parent_lct; } /* drm_dp_add_mst_branch_device(lct, rad) */ if (lct > 1 && (lct / 2) > RAD_HEAP) { heap_oob = 1; if (first_heap_oob < 0) first_heap_oob = parent_lct; } /* set_hdr_from_dst_qlock(): mstb->lct == lct */ if (lct > 1 && (lct / 2) > 8) { hdr_oob = 1; if (first_hdr_oob < 0) first_hdr_oob = parent_lct; } /* drm_dp_mst_dump_mstb(): tabs = mstb->lct == lct */ if (lct >= PREFIX) { prefix_oob = 1; if (first_prefix_oob < 0) first_prefix_oob = parent_lct; } printf("parent_lct=%2d child_lct=%2d rad_idx=%2d " "stack_rad6:%-3s heap_rad8:%-3s hdr_rad8:%-3s prefix10:%-3s\n", parent_lct, lct, idx, stack_oob ? "OOB" : "ok", heap_oob ? "OOB" : "ok", hdr_oob ? "OOB" : "ok", prefix_oob? "OOB" : "ok"); } printf("\nfirst OOB per sink:\n"); printf(" drm_dp_calculate_rad stack rad[6] : parent_lct=%d (chain of %d hubs)\n", first_stack_oob, first_stack_oob - 1); printf(" drm_dp_add_mst_branch_device rad[8] : parent_lct=%d\n", first_heap_oob); printf(" set_hdr_from_dst_qlock hdr rad[8] : parent_lct=%d\n", first_hdr_oob); printf(" drm_dp_mst_dump_mstb prefix[10] : parent_lct=%d (debugfs, root)\n", first_prefix_oob); /* * The recursion itself: drm_dp_send_link_address() reply loop calls * drm_dp_add_port() which calls drm_dp_send_link_address() for the * child. One nested kernel frame-set per level, no depth limit. * DFBSD kernel thread stack = 16KB (KSTACK_PAGES=4, unless configured * otherwise). Frame-set (send_link_address + add_port + setup_pdt + * build helpers) is on the order of 200-400 bytes per level. */ printf("\nrecursion: ~300 bytes of kernel stack per topology level;\n"); printf("16KB kernel stack exhausts at roughly depth %d (%d hub levels deep).\n", 16384 / 300, 16384 / 300 - 1); return 0; } |