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