DF-3886 / sbmsg_accum_model.c
/* DF-3886 model: drm_dp_sideband_msg_build() accumulates curlen into * msg->msg[256] without any bound. * * Kernel code (sys/dev/drm/drm_dp_mst_topology.c:371-377): * * if (msg->curchunk_idx >= msg->curchunk_len) { * crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1); * memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1); * msg->curlen += msg->curchunk_len - 1; * } * * curchunk_len is the hub-controlled 6-bit msg_len from the sideband header * (up to 63 => up to 62 bytes appended per completed chunk). curlen is a u8 * and is NEVER validated against sizeof(msg->msg) == 256. A reply delivered * in >= 5 chunks (e.g. a crafted LINK_ADDRESS with 15 ports: 18 + 15*21 = 333 * bytes of body) walks the append offset past 256 and writes up to 61 * hub-controlled bytes past msg[256] -- over curchunk_len/curlen/have_somt/ * have_eomt/initial_hdr and into the adjacent up_req_recv of the same * topology manager (struct drm_dp_mst_topology_mgr, * drm_dp_mst_helper.h:464-470). * * Distinct sink from DF-1264 (which is the chunk[48] array in the non-hdr * continuation branch). This model reproduces the accumulation arithmetic. */ #include <stdio.h> #include <string.h> #include <stdint.h> struct rx_model { /* mirrors struct drm_dp_sideband_msg_rx */ uint8_t chunk[48]; uint8_t msg[256]; uint8_t curchunk_len; uint8_t curchunk_idx; uint8_t curchunk_hdrlen; uint8_t curlen; uint8_t have_somt; uint8_t have_eomt; uint8_t initial_hdr[24]; uint8_t up_req_recv_chunk0; /* first byte of the adjacent up_req_recv */ }; int main(void) { struct rx_model m; int chunk_no, max_spill = 0, first_oob_chunk = -1; int n_chunks = 8; /* a >256-byte reply needs >= 5 chunks */ uint8_t fill = 0xA5; /* hub-controlled reply bytes */ memset(&m, 0, sizeof(m)); for (chunk_no = 0; chunk_no < n_chunks; chunk_no++) { int curchunk_len = 63; /* max 6-bit msg_len (hdr->msg_len) */ int append = curchunk_len - 1; int dst = m.curlen; int end = dst + append; if (dst < 256 && end > 256) { int spill = end - 256; if (spill > max_spill) max_spill = spill; if (first_oob_chunk < 0) first_oob_chunk = chunk_no + 1; printf("chunk %d: memcpy(&msg[%d], chunk, %d) writes %d bytes " "past msg[256] (offsets 256..%d)\n", chunk_no + 1, dst, append, spill, end - 1); /* emulate the write to show what gets clobbered */ memset(((uint8_t *)&m) + __builtin_offsetof(struct rx_model, msg) + dst, fill, append > sizeof(m) ? sizeof(m) : append); } else if (dst >= 256) { printf("chunk %d: append at &msg[%d] with curlen already wrapped\n", chunk_no + 1, dst); } else { printf("chunk %d: append %d bytes at &msg[%d] (in bounds, end=%d)\n", chunk_no + 1, append, dst, end); memset(m.msg + dst, fill, append); } m.curlen = (uint8_t)(m.curlen + append); /* u8 wrap, as in kernel */ } printf("\nresult: first OOB append at chunk %d; max spill %d bytes past msg[256]\n", first_oob_chunk, max_spill); printf("clobbered after msg[256]: curchunk_len=%02x curchunk_idx=%02x curlen=%02x " "have_somt=%02x have_eomt=%02x initial_hdr[0]=%02x up_req_recv.chunk[0]=%02x\n", m.curchunk_len, m.curchunk_idx, m.curlen, m.have_somt, m.have_eomt, m.initial_hdr[0], m.up_req_recv_chunk0); return 0; } |