• 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_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