Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2017 Richard Palethorpe <rpalethorpe@suse.com>
*/
/*
* 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 "config.h"
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <stdlib.h>
#include <errno.h>
#include "tst_test.h"
#include "tst_safe_net.h"
#include "tst_timer.h"
#include "tst_fuzzy_sync.h"
/* The bug was present in the kernel before recvmmsg was exposed by glibc */
#include "lapi/syscalls.h"
#include "lapi/socket.h"
#define MSG "abcdefghijklmnop"
#define RECV_TIMEOUT 1
#define ATTEMPTS 0x1FFFFF
static volatile 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;
static void *send_and_close(void *);
static void setup(void)
{
fzsync_pair.min_samples = 10000;
tst_syscall(__NR_recvmmsg, 0, 0, 0, 0, 0);
tst_fzsync_pair_init(&fzsync_pair);
}
static void cleanup(void)
{
close(socket_fds[0]);
close(socket_fds[1]);
tst_fzsync_pair_cleanup(&fzsync_pair);
}
static void *send_and_close(void *arg)
{
while (tst_fzsync_run_b(&fzsync_pair)) {
tst_fzsync_wait_b(&fzsync_pair);
send(socket_fds[0], MSG, sizeof(MSG), 0);
send(socket_fds[0], MSG, sizeof(MSG), 0);
close(socket_fds[0]);
tst_fzsync_start_race_b(&fzsync_pair);
close(socket_fds[1]);
tst_fzsync_end_race_b(&fzsync_pair);
}
return arg;
}
static void run(void)
{
int stat, too_early_count = 0;
msghdrs[0].msg_hdr.msg_iov->iov_base = (void *)&rbuf;
tst_fzsync_pair_reset(&fzsync_pair, send_and_close);
while (tst_fzsync_run_a(&fzsync_pair)) {
SAFE_SOCKETPAIR(AF_LOCAL, SOCK_DGRAM, 0, (int *)socket_fds);
tst_fzsync_wait_a(&fzsync_pair);
tst_fzsync_start_race_a(&fzsync_pair);
stat = tst_syscall(__NR_recvmmsg,
socket_fds[1], msghdrs, 2, 0, &timeout);
tst_fzsync_end_race_a(&fzsync_pair);
if (stat == 0)
tst_res(TWARN, "No messages received, should be one");
else if (stat < 0) {
if (errno != EBADF) {
tst_res(TWARN | TERRNO,
"recvmmsg failed unexpectedly");
} else {
tst_fzsync_pair_add_bias(&fzsync_pair, 1);
too_early_count++;
}
}
}
tst_res(TPASS, "Nothing bad happened, probably");
tst_res(TINFO, "Socket was closed too early %d times", too_early_count);
}
static struct tst_test test = {
.test_all = run,
.setup = setup,
.cleanup = cleanup,
.min_kver = "2.6.33",
.max_runtime = 60,
.tags = (const struct tst_tag[]) {
{"linux-git", "a2e2725541fa"},
{"CVE", "2016-7117"},
{}
}
};