From mboxrd@z Thu Jan 1 00:00:00 1970 From: Richard Palethorpe Date: Fri, 23 Jun 2017 14:22:04 +0200 Subject: [LTP] [PATCH v3 2/9] Test for vulnerability CVE-2016-7117 in recvmmsg error return path In-Reply-To: <20170623122211.29575-1-rpalethorpe@suse.com> References: <20170623122211.29575-1-rpalethorpe@suse.com> Message-ID: <20170623122211.29575-3-rpalethorpe@suse.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: ltp@lists.linux.it Signed-off-by: Richard Palethorpe --- configure.ac | 1 + m4/ltp-mmsghdr.m4 | 22 ++++++ testcases/cve/cve-2016-7117.c | 158 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 m4/ltp-mmsghdr.m4 create mode 100644 testcases/cve/cve-2016-7117.c diff --git a/configure.ac b/configure.ac index cbe01d34f..326da8ece 100644 --- a/configure.ac +++ b/configure.ac @@ -192,5 +192,6 @@ LTP_CHECK_EPOLL_PWAIT LTP_CHECK_KEYUTILS_SUPPORT LTP_CHECK_SYNC_ADD_AND_FETCH LTP_CHECK_BUILTIN_CLEAR_CACHE +LTP_CHECK_MMSGHDR AC_OUTPUT diff --git a/m4/ltp-mmsghdr.m4 b/m4/ltp-mmsghdr.m4 new file mode 100644 index 000000000..05522180e --- /dev/null +++ b/m4/ltp-mmsghdr.m4 @@ -0,0 +1,22 @@ +dnl Copyright (c) 2017 Richard Palethorpe +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +dnl the GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program. If not, see . + +AC_DEFUN([LTP_CHECK_MMSGHDR],[ +AC_CHECK_TYPES([struct mmsghdr],,,[ +#define _GNU_SOURCE +#include +#include +]) +]) diff --git a/testcases/cve/cve-2016-7117.c b/testcases/cve/cve-2016-7117.c new file mode 100644 index 000000000..25937edd1 --- /dev/null +++ b/testcases/cve/cve-2016-7117.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017 Richard Palethorpe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * CVE-2016-7117 + * + * This tests for a use after free caused by a race between recvmmsg() and + * close(). The exit path for recvmmsg() in (a2e2725541f: net: Introduce + * recvmmsg socket syscall) called fput() on the active file descriptor before + * checking the error state and setting the socket's error field. + * + * If one or more messages are received by recvmmsg() followed by one which + * fails, the socket's error field will be set. If just after recvmmsg() calls + * fput(), a call to close() is made on the same file descriptor there is a + * race between close() releasing the socket object and recvmmsg() setting its + * error field. + * + * fput() does not release a file descriptor's resources (e.g. a socket) + * immediatly, it queues them to be released just before a system call returns + * to user land. So the close() system call must call fput() after it is + * called in recvmmsg(), exit and release the resources all before the socket + * error is set. + * + * Usually if the vulnerability is present the test will be killed with a + * kernel null pointer exception. However this is not guaranteed to happen + * every time. + * + * The following was used for reference + * https://blog.lizzie.io/notes-about-cve-2016-7117.html + */ + +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_net.h" +#include "tst_safe_pthread.h" +#include "tst_timer.h" +#include "tst_fuzzy_sync.h" + +/* The bug was present in the kernel before recvmmsg was exposed by glibc */ +#include "linux_syscall_numbers.h" + +#define MSG "abcdefghijklmnop" +#define RECV_TIMEOUT 1 +#define ATTEMPTS 0x1FFFFF + +#ifndef HAVE_STRUCT_MMSGHDR +struct mmsghdr { + struct msghdr msg_hdr; + unsigned int msg_len; +}; +#endif + +static int socket_fds[2]; +static struct mmsghdr msghdrs[2] = { + { + .msg_hdr = { + .msg_iov = &(struct iovec) { + .iov_len = sizeof(MSG), + }, + .msg_iovlen = 1 + } + }, + { + .msg_hdr = { + .msg_iov = &(struct iovec) { + .iov_base = (void *)(0xbadadd), + .iov_len = ~0, + }, + .msg_iovlen = 1 + } + } +}; +static char rbuf[sizeof(MSG)]; +static struct timespec timeout = { .tv_sec = RECV_TIMEOUT }; +static struct tst_fzsync_pair fzsync_pair = TST_FZSYNC_PAIR_INIT; + +static void cleanup(void) +{ + close(socket_fds[0]); + close(socket_fds[1]); +} + +static void *send_and_close(void *arg) +{ + send(socket_fds[0], MSG, sizeof(MSG), 0); + send(socket_fds[0], MSG, sizeof(MSG), 0); + + tst_fzsync_delay_b(&fzsync_pair); + + close(socket_fds[0]); + close(socket_fds[1]); + tst_fzsync_time_b(&fzsync_pair); + + return arg; +} + +static void run(void) +{ + pthread_t pt_send; + int i, stat, too_early_count = 0; + + msghdrs[0].msg_hdr.msg_iov->iov_base = (void *)&rbuf; + + for (i = 1; i < ATTEMPTS; i++) { + if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, socket_fds)) + tst_brk(TBROK | TERRNO, "Socket creation failed"); + + SAFE_PTHREAD_CREATE(&pt_send, 0, send_and_close, 0); + + tst_fzsync_delay_a(&fzsync_pair); + + stat = tst_syscall(__NR_recvmmsg, + socket_fds[1], msghdrs, 2, 0, &timeout); + tst_fzsync_time_a(&fzsync_pair); + if (stat < 0 && errno == EBADF) + too_early_count++; + else if (stat == 0) + tst_res(TWARN, "No messages received, should be one"); + else if (stat < 0) + tst_res(TWARN | TERRNO, "recvmmsg failed unexpectedly"); + + SAFE_PTHREAD_JOIN(pt_send, 0); + + tst_fzsync_pair_update(i, &fzsync_pair); + if (!(i & 0x7FFFF)) { + tst_res(TINFO, "Too early: %.1f%%", + 100 * too_early_count / (float)i); + tst_fzsync_pair_info(&fzsync_pair); + } + } + + tst_res(TPASS, "Nothing happened after %d attempts", ATTEMPTS); +} + +static struct tst_test test = { + .test_all = run, + .cleanup = cleanup, + .min_kver = "2.6.33", +}; -- 2.12.2