• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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