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_TRY_FILE_PRINTF("/proc/sys/user/max_user_namespaces", "%d", 10);
47
48 SAFE_UNSHARE(CLONE_NEWUSER);
49 SAFE_UNSHARE(CLONE_NEWNET);
50 SAFE_FILE_PRINTF("/proc/self/setgroups", "deny");
51 SAFE_FILE_PRINTF("/proc/self/uid_map", "0 %d 1", real_uid);
52 SAFE_FILE_PRINTF("/proc/self/gid_map", "0 %d 1", real_gid);
53
54 sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
55 strcpy(ifr.ifr_name, "lo");
56 ifr.ifr_flags = IFF_UP;
57 SAFE_IOCTL(sock, SIOCSIFFLAGS, &ifr);
58 SAFE_IOCTL(sock, SIOCGIFINDEX, &ifr);
59 SAFE_CLOSE(sock);
60
61 memset(buf, 0x42, BUFSIZE);
62
63 bind_addr.sll_family = AF_PACKET;
64 bind_addr.sll_protocol = htons(ETH_P_ALL);
65 bind_addr.sll_ifindex = ifr.ifr_ifindex;
66
67 addr.sll_family = AF_PACKET;
68 addr.sll_ifindex = ifr.ifr_ifindex;
69 addr.sll_halen = ETH_ALEN;
70 }
71
72 /* Test for commit bcc5364bdcfe (cap PACKET_RESERVE to INT_MAX) */
check_tiny_frame(void)73 static int check_tiny_frame(void)
74 {
75 unsigned int val = (UINT_MAX - TPACKET2_HDRLEN) + 1;
76 struct tpacket_req tpreq;
77
78 tpreq.tp_block_size = SAFE_SYSCONF(_SC_PAGESIZE);
79 tpreq.tp_frame_size = TPACKET_ALIGNMENT;
80 tpreq.tp_block_nr = 1;
81 tpreq.tp_frame_nr = (tpreq.tp_block_size * tpreq.tp_block_nr) /
82 tpreq.tp_frame_size;
83
84 dst_sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
85 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_VERSION, TPACKET_V2);
86 TEST(setsockopt(dst_sock, SOL_PACKET, PACKET_RESERVE, &val,
87 sizeof(val)));
88
89 if (TST_RET == -1 && TST_ERR == EINVAL) {
90 SAFE_CLOSE(dst_sock);
91 tst_res(TPASS | TTERRNO,
92 "setsockopt(PACKET_RESERVE) value is capped");
93 return 0;
94 }
95
96 if (TST_RET == -1) {
97 tst_brk(TBROK | TTERRNO,
98 "setsockopt(PACKET_RESERVE): unexpected error");
99 }
100
101 if (TST_RET) {
102 tst_brk(TBROK | TTERRNO,
103 "Invalid setsockopt(PACKET_RESERVE) return value");
104 }
105
106 tst_res(TINFO, "setsockopt(PACKET_RESERVE) accepted too large value");
107 tst_res(TINFO, "Checking whether this will cause integer overflow...");
108 TEST(setsockopt(dst_sock, SOL_PACKET, PACKET_RX_RING, &tpreq,
109 sizeof(tpreq)));
110 SAFE_CLOSE(dst_sock);
111
112 if (!TST_RET) {
113 tst_res(TFAIL, "setsockopt(PACKET_RX_RING) accepted frame "
114 "size smaller than packet header");
115 return 0;
116 }
117
118 if (TST_RET != -1) {
119 tst_brk(TBROK | TTERRNO,
120 "Invalid setsockopt(PACKET_RX_RING) return value");
121 }
122
123 if (TST_ERR != EINVAL) {
124 tst_brk(TBROK | TTERRNO,
125 "setsockopt(PACKET_RX_RING): unexpeced error");
126 }
127
128 tst_res(TPASS | TTERRNO, "setsockopt(PACKET_RX_RING) frame size check "
129 "rejects values smaller than packet header");
130 /* This test case should not cause kernel taint, skip taint check */
131 return 0;
132 }
133
134 /* Test for commit acf69c946233 (drop packet if netoff overflows) */
check_vnet_hdr(void)135 static int check_vnet_hdr(void)
136 {
137 struct tpacket_req tpreq;
138 size_t blocksize = 0x800000, pagesize = SAFE_SYSCONF(_SC_PAGESIZE);
139
140 /* Make sure blocksize is big enough and pagesize aligned */
141 if (blocksize % pagesize)
142 blocksize += pagesize - (blocksize % pagesize);
143
144 tpreq.tp_block_size = blocksize;
145 tpreq.tp_frame_size = 0x11000;
146 tpreq.tp_block_nr = 1;
147 tpreq.tp_frame_nr = (tpreq.tp_block_size * tpreq.tp_block_nr) /
148 tpreq.tp_frame_size;
149
150 dst_sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
151 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_VERSION, TPACKET_V2);
152 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_VNET_HDR, 1);
153 SAFE_SETSOCKOPT_INT(dst_sock, SOL_PACKET, PACKET_RESERVE, 0xffff - 75);
154 TEST(setsockopt(dst_sock, SOL_PACKET, PACKET_RX_RING, &tpreq,
155 sizeof(tpreq)));
156
157 if (TST_RET == -1 && TST_ERR == EINVAL) {
158 SAFE_CLOSE(dst_sock);
159 tst_res(TCONF, "PACKET_VNET_HDR and PACKET_RX_RING not "
160 "supported together");
161 return 0;
162 }
163
164 if (TST_RET == -1) {
165 tst_brk(TBROK | TTERRNO,
166 "setsockopt(PACKET_RX_RING): unexpected error");
167 }
168
169 if (TST_RET) {
170 tst_brk(TBROK | TTERRNO,
171 "Invalid setsockopt(PACKET_RX_RING) return value");
172 }
173
174 SAFE_BIND(dst_sock, (struct sockaddr *)&bind_addr, sizeof(addr));
175
176 sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
177 SAFE_SENDTO(1, sock, buf, BUFSIZE, 0, (struct sockaddr *)&addr,
178 sizeof(addr));
179
180 SAFE_CLOSE(sock);
181 SAFE_CLOSE(dst_sock);
182 return 1; /* Continue to taint check */
183 }
184
185 static int (*testcase_list[])(void) = {check_tiny_frame, check_vnet_hdr};
186
run(unsigned int n)187 static void run(unsigned int n)
188 {
189 if (!testcase_list[n]())
190 return;
191
192 if (tst_taint_check()) {
193 tst_res(TFAIL, "Kernel is vulnerable");
194 return;
195 }
196
197 tst_res(TPASS, "Nothing bad happened, probably");
198 }
199
cleanup(void)200 static void cleanup(void)
201 {
202 if (sock != -1)
203 SAFE_CLOSE(sock);
204
205 if (dst_sock != -1)
206 SAFE_CLOSE(dst_sock);
207 }
208
209 static struct tst_test test = {
210 .test = run,
211 .tcnt = ARRAY_SIZE(testcase_list),
212 .setup = setup,
213 .cleanup = cleanup,
214 .taint_check = TST_TAINT_W | TST_TAINT_D,
215 .needs_kconfigs = (const char *[]) {
216 "CONFIG_USER_NS=y",
217 "CONFIG_NET_NS=y",
218 NULL
219 },
220 .save_restore = (const char * const[]) {
221 "?/proc/sys/user/max_user_namespaces",
222 NULL,
223 },
224 .tags = (const struct tst_tag[]) {
225 {"linux-git", "bcc5364bdcfe"},
226 {"linux-git", "acf69c946233"},
227 {"CVE", "2020-14386"},
228 {}
229 }
230 };
231