-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Copy pathcve-2016-7117.c
162 lines (138 loc) · 4.06 KB
/
cve-2016-7117.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2017 Richard Palethorpe <rpalethorpe@suse.com>
*/
/*\
* [Description]
*
* Test for CVE-2016-7117, which was fixed in kernel v4.6:
* 34b88a68f26a ("net: Fix use after free in the recvmmsg exit path").
*
* 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,
.runtime = 60,
.tags = (const struct tst_tag[]) {
{"linux-git", "34b88a68f26a"},
{"CVE", "2016-7117"},
{}
}
};