DF-3988 / trigger.c
/* * DF-3988 trigger.c — model of dc_link_dp_set_test_pattern's pipe scan * (dc_link_dp.c:2605-2610) dereferencing pipes[i].stream->sink without a * NULL check, plus the wrong-object blank at :2653. * * HARDWARE PRECONDITION (absent on audit guest): amdgpu DC + DP sink that * raises DEVICE_SERVICE_IRQ.AUTOMATED_TEST (link or PHY test request). * NO ACTIVE STREAM IS REQUIRED ON THE TESTED LINK: * dc_link_handle_hpd_irq :2045 -> handle_automated_test :1990/:1994 * -> dp_test_send_link_test_pattern / dp_test_send_phy_test_pattern * -> dc_link_dp_set_test_pattern :2588 * :2595 pipes = link->dc->current_state->res_ctx.pipe_ctx; * :2605 for (i = 0; i < MAX_PIPES; i++) * :2606 if (pipes[i].stream->sink->link == link) <-- NULL deref * pipe_ctx[].stream is NULL for every inactive pipe; after boot with no * display commit, or after any configuration that leaves holes before the * matching pipe (e.g. monitor unplug leaves stream on pipe1, pipe0 NULL), * the very first hole dereferences NULL->sink. * Also :2653 pipes->stream_res.stream_enc->funcs->dp_blank(...) uses * pipes[0] instead of the matched pipe_ctx (wrong object; NULL deref when * pipe0 has no stream_enc). */ #include <stdio.h> #include <stdint.h> #include <stddef.h> #define MAX_PIPES 6 struct sink { int link; }; struct stream_s { struct sink *sink; }; struct pipe_m { struct stream_s *stream; int has_enc; }; static struct sink sinks[2] = { { .link = 7 }, { .link = 9 } }; /* hole replica: pipe0 = no stream (post-unplug hole), pipe1 = stream on * link 7 (the tested link), rest empty */ static struct pipe_m pipes[MAX_PIPES] = { { .stream = NULL, .has_enc = 0 }, { .stream = NULL /*fake*/, .has_enc = 1 }, }; int main(void) { struct pipe_m *pipe_ctx; int i, matched = -1; /* patch in the fake stream after static init (avoid const issues) */ static struct stream_s s1 = { .sink = &sinks[0] }; pipes[1].stream = &s1; printf("DF-3988 model: dc_link_dp.c:2605-2610 pipe scan\n"); printf(" link under test = %d; pipes[0].stream = NULL, " "match at pipes[1]\n", sinks[0].link); /* exact loop shape of :2605-2610 */ for (i = 0; i < MAX_PIPES; i++) { printf(" i=%d pipes[%d].stream=%p -> ...->sink->link: ", i, i, (void *)pipes[i].stream); if (pipes[i].stream == NULL) { printf("NULL DEREF (panic) " "[KERNEL: pipes[i].stream->sink]\n"); return (0); } if (pipes[i].stream->sink->link == sinks[0].link) { matched = i; printf("match\n"); break; } printf("no match\n"); } pipe_ctx = &pipes[matched >= 0 ? matched : 0]; /* :2653 uses pipes[0] (not pipe_ctx) for the blank */ if (!pipes[0].has_enc) printf(" :2653 pipes->stream_res.stream_enc == NULL " "-> second NULL DEREF (wrong-object access)\n"); (void)pipe_ctx; printf("TRIGGER_RC=0 (model OK; hardware-gated on this guest: " "no AMD GPU)\n"); return (0); } |