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