1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 SUSE LLC <mdoucha@suse.cz>
4 */
5
6 /*
7 * CVE-2020-14386
8 *
9 * Check for vulnerability in tpacket_rcv() which allows an unprivileged user
10 * to write arbitrary data to a memory area outside the allocated packet
11 * buffer. Kernel crash fixed in:
12 *
13 * commit acf69c946233259ab4d64f8869d4037a198c7f06
14 * Author: Or Cohen <orcohen@paloaltonetworks.com>
15 * Date: Thu Sep 3 21:05:28 2020 -0700
16 *
17 * net/packet: fix overflow in tpacket_rcv
18 */
19
20 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <limits.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/ioctl.h>
26 #include <net/if.h>
27 #include <net/ethernet.h>
28 #include <sched.h>
29
30 #include "tst_test.h"
31 #include "tst_net.h"
32 #include "lapi/if_packet.h"
33
34 #define BUFSIZE 1024
35
36 static int dst_sock = -1, sock = -1;
37 static unsigned char buf[BUFSIZE];
38 static struct sockaddr_ll bind_addr, addr;
39
setup(void)40 static void setup(void)
41 {
42 int real_uid = getuid();
43 int real_gid = getgid();
44 struct ifreq ifr;
45
46 SAFE_UNSHARE(CLONE_NEWUSER);
47 SAFE_UNSHARE(CLONE_NEWNET);
48 SAFE_FILE_PRINTF("/proc/self/setgroups", "deny");
49 SAFE_FILE_PRINTF("/proc/self/uid_map", "0 %d 1", real_uid);
50 SAFE_FILE_PRINTF("/proc/self/gid_map", "0 %d 1", real_gid);
51
52 sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
53 strcpy(ifr.ifr_name, "lo");
54 ifr.ifr_flags = IFF_UP;
55 SAFE_IOCTL(sock, SIOCSIFFLAGS, &ifr);
56 SAFE_IOCTL(sock, SIOCGIFINDEX, &ifr);
57 SAFE_CLOSE(sock);
58
59 memset(buf, 0x42, BUFSIZE);
60
61 bind_addr.sll_family = AF_PACKET;
62 bind_addr.sll_protocol = htons(ETH_P_ALL);
63 bind_addr.sll_ifindex = ifr.ifr_ifindex;
64
65 addr.sll_family = AF_PACKET;
66 addr.sll_ifindex = ifr.ifr_ifindex;
67 addr.sll_halen = ETH_ALEN;
68 }
69
70 /* Test for commit bcc5364bdcfe (cap PACKET_RESERVE to INT_MAX) */
check_tiny_frame(void)71 static int check_tiny_frame(void)
72 {
73 unsigned int val = (UINT_MAX - TPACKET2_HDRLEN) + 1;
74 struct tpacket_req tpreq;
75
76 tpreq.tp_block_size = SAFE_SYSCONF(_SC_PAGESIZE);
77 tpreq.tp_frame_size = TPACKET_ALIGNMENT;
78 tpreq.tp_block_nr = 1;
79 tpreq.tp_frame_nr = (tpreq.tp_block_size * tpreq.tp_block_nr) /
80 tpreq.tp_frame_size;
81
82 dst_sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
83 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_VERSION, TPACKET_V2);
84 TEST(setsockopt(dst_sock, SOL_PACKET, PACKET_RESERVE, &val,
85 sizeof(val)));
86
87 if (TST_RET == -1 && TST_ERR == EINVAL) {
88 SAFE_CLOSE(dst_sock);
89 tst_res(TPASS | TTERRNO,
90 "setsockopt(PACKET_RESERVE) value is capped");
91 return 0;
92 }
93
94 if (TST_RET == -1) {
95 tst_brk(TBROK | TTERRNO,
96 "setsockopt(PACKET_RESERVE): unexpected error");
97 }
98
99 if (TST_RET) {
100 tst_brk(TBROK | TTERRNO,
101 "Invalid setsockopt(PACKET_RESERVE) return value");
102 }
103
104 tst_res(TINFO, "setsockopt(PACKET_RESERVE) accepted too large value");
105 tst_res(TINFO, "Checking whether this will cause integer overflow...");
106 TEST(setsockopt(dst_sock, SOL_PACKET, PACKET_RX_RING, &tpreq,
107 sizeof(tpreq)));
108 SAFE_CLOSE(dst_sock);
109
110 if (!TST_RET) {
111 tst_res(TFAIL, "setsockopt(PACKET_RX_RING) accepted frame "
112 "size smaller than packet header");
113 return 0;
114 }
115
116 if (TST_RET != -1) {
117 tst_brk(TBROK | TTERRNO,
118 "Invalid setsockopt(PACKET_RX_RING) return value");
119 }
120
121 if (TST_ERR != EINVAL) {
122 tst_brk(TBROK | TTERRNO,
123 "setsockopt(PACKET_RX_RING): unexpeced error");
124 }
125
126 tst_res(TPASS | TTERRNO, "setsockopt(PACKET_RX_RING) frame size check "
127 "rejects values smaller than packet header");
128 /* This test case should not cause kernel taint, skip taint check */
129 return 0;
130 }
131
132 /* Test for commit acf69c946233 (drop packet if netoff overflows) */
check_vnet_hdr(void)133 static int check_vnet_hdr(void)
134 {
135 struct tpacket_req tpreq;
136 size_t blocksize = 0x800000, pagesize = SAFE_SYSCONF(_SC_PAGESIZE);
137
138 /* Make sure blocksize is big enough and pagesize aligned */
139 if (blocksize % pagesize)
140 blocksize += pagesize - (blocksize % pagesize);
141
142 tpreq.tp_block_size = blocksize;
143 tpreq.tp_frame_size = 0x11000;
144 tpreq.tp_block_nr = 1;
145 tpreq.tp_frame_nr = (tpreq.tp_block_size * tpreq.tp_block_nr) /
146 tpreq.tp_frame_size;
147
148 dst_sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
149 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_VERSION, TPACKET_V2);
150 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_VNET_HDR, 1);
151 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_RESERVE, 0xffff - 75);
152 TEST(setsockopt(dst_sock, SOL_PACKET, PACKET_RX_RING, &tpreq,
153 sizeof(tpreq)));
154
155 if (TST_RET == -1 && TST_ERR == EINVAL) {
156 SAFE_CLOSE(dst_sock);
157 tst_res(TCONF, "PACKET_VNET_HDR and PACKET_RX_RING not "
158 "supported together");
159 return 0;
160 }
161
162 if (TST_RET == -1) {
163 tst_brk(TBROK | TTERRNO,
164 "setsockopt(PACKET_RX_RING): unexpected error");
165 }
166
167 if (TST_RET) {
168 tst_brk(TBROK | TTERRNO,
169 "Invalid setsockopt(PACKET_RX_RING) return value");
170 }
171
172 SAFE_BIND(dst_sock, (struct sockaddr *)&bind_addr, sizeof(addr));
173
174 sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
175 SAFE_SENDTO(1, sock, buf, BUFSIZE, 0, (struct sockaddr *)&addr,
176 sizeof(addr));
177
178 SAFE_CLOSE(sock);
179 SAFE_CLOSE(dst_sock);
180 return 1; /* Continue to taint check */
181 }
182
183 static int (*testcase_list[])(void) = {check_tiny_frame, check_vnet_hdr};
184
run(unsigned int n)185 static void run(unsigned int n)
186 {
187 if (!testcase_list[n]())
188 return;
189
190 if (tst_taint_check()) {
191 tst_res(TFAIL, "Kernel is vulnerable");
192 return;
193 }
194
195 tst_res(TPASS, "Nothing bad happened, probably");
196 }
197
cleanup(void)198 static void cleanup(void)
199 {
200 if (sock != -1)
201 SAFE_CLOSE(sock);
202
203 if (dst_sock != -1)
204 SAFE_CLOSE(dst_sock);
205 }
206
207 static struct tst_test test = {
208 .test = run,
209 .tcnt = ARRAY_SIZE(testcase_list),
210 .setup = setup,
211 .cleanup = cleanup,
212 .taint_check = TST_TAINT_W | TST_TAINT_D,
213 .needs_kconfigs = (const char *[]) {
214 "CONFIG_USER_NS=y",
215 "CONFIG_NET_NS=y",
216 NULL
217 },
218 .tags = (const struct tst_tag[]) {
219 {"linux-git", "bcc5364bdcfe"},
220 {"linux-git", "acf69c946233"},
221 {"CVE", "2020-14386"},
222 {}
223 }
224 };
225