1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2020 SUSE LLC <mdoucha@suse.cz>
4 */
5
6/*
7 * CVE-2017-1000111
8 *
9 * Check for race condition between packet_set_ring() and tp_reserve.
10 * The race allows you to set tp_reserve bigger than ring buffer size.
11 * While this will cause truncation of all incoming packets to 0 bytes,
12 * sanity checks in tpacket_rcv() prevent any exploitable buffer overflows.
13 * Race fixed in:
14 *
15 *  commit c27927e372f0785f3303e8fad94b85945e2c97b7 (HEAD)
16 *  Author: Willem de Bruijn <willemb@google.com>
17 *  Date:   Thu Aug 10 12:41:58 2017 -0400
18 *
19 *  packet: fix tp_reserve race in packet_set_ring
20 */
21
22#include <unistd.h>
23#include <sys/types.h>
24#include <sys/socket.h>
25
26#include "tst_test.h"
27#include "tst_fuzzy_sync.h"
28#include "lapi/if_packet.h"
29#include "lapi/if_ether.h"
30
31static int sock = -1;
32static unsigned int pagesize;
33static struct tst_fzsync_pair fzsync_pair;
34
35static void setup(void)
36{
37	pagesize = SAFE_SYSCONF(_SC_PAGESIZE);
38	tst_setup_netns();
39
40	/*
41	 * Reproducing the bug on unpatched system takes <15 loops. The test
42	 * is slow and the bug is mostly harmless so don't waste too much
43	 * time.
44	 */
45	fzsync_pair.exec_loops = 500;
46	tst_fzsync_pair_init(&fzsync_pair);
47}
48
49static void *thread_run(void *arg)
50{
51	unsigned int val = 1 << 30;
52
53	while (tst_fzsync_run_b(&fzsync_pair)) {
54		tst_fzsync_start_race_b(&fzsync_pair);
55		setsockopt(sock, SOL_PACKET, PACKET_RESERVE, &val, sizeof(val));
56		tst_fzsync_end_race_b(&fzsync_pair);
57	}
58
59	return arg;
60}
61
62static void run(void)
63{
64	unsigned int val, version = TPACKET_V3;
65	socklen_t vsize = sizeof(val);
66	struct tpacket_req3 req = {
67		.tp_block_size = pagesize,
68		.tp_block_nr = 1,
69		.tp_frame_size = pagesize,
70		.tp_frame_nr = 1,
71		.tp_retire_blk_tov = 100
72	};
73
74	tst_fzsync_pair_reset(&fzsync_pair, thread_run);
75
76	while (tst_fzsync_run_a(&fzsync_pair)) {
77		sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
78		TEST(setsockopt(sock, SOL_PACKET, PACKET_VERSION, &version,
79			sizeof(version)));
80
81		if (TST_RET == -1 && TST_ERR == EINVAL)
82			tst_brk(TCONF | TTERRNO, "TPACKET_V3 not supported");
83
84		if (TST_RET) {
85			tst_brk(TBROK | TTERRNO,
86				"setsockopt(PACKET_VERSION, TPACKET_V3");
87		}
88
89		tst_fzsync_start_race_a(&fzsync_pair);
90		TEST(setsockopt(sock, SOL_PACKET, PACKET_RX_RING, &req,
91			sizeof(req)));
92		tst_fzsync_end_race_a(&fzsync_pair);
93
94		SAFE_GETSOCKOPT(sock, SOL_PACKET, PACKET_RESERVE, &val, &vsize);
95		SAFE_CLOSE(sock);
96
97		if (TST_RET == -1 && TST_ERR == EINVAL) {
98			tst_fzsync_pair_add_bias(&fzsync_pair, 1);
99			continue;
100		}
101
102		if (TST_RET) {
103			tst_brk(TBROK | TTERRNO,
104				"Invalid setsockopt() return value");
105		}
106
107		if (val > req.tp_block_size) {
108			tst_res(TFAIL, "PACKET_RESERVE checks bypassed");
109			return;
110		}
111	}
112
113	tst_res(TPASS, "Cannot reproduce bug");
114}
115
116static void cleanup(void)
117{
118	tst_fzsync_pair_cleanup(&fzsync_pair);
119
120	if (sock >= 0)
121		SAFE_CLOSE(sock);
122}
123
124static struct tst_test test = {
125	.test_all = run,
126	.setup = setup,
127	.cleanup = cleanup,
128	.max_runtime = 150,
129	.needs_kconfigs = (const char *[]) {
130		"CONFIG_USER_NS=y",
131		"CONFIG_NET_NS=y",
132		NULL
133	},
134	.save_restore = (const struct tst_path_val[]) {
135		{"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP},
136		{}
137	},
138	.tags = (const struct tst_tag[]) {
139		{"linux-git", "c27927e372f0"},
140		{"CVE", "2017-1000111"},
141		{}
142	}
143};
144