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