1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2020 SUSE LLC <[email protected]>
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 <[email protected]>
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
31 static int sock = -1;
32 static unsigned int pagesize;
33 static struct tst_fzsync_pair fzsync_pair;
34
setup(void)35 static 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
thread_run(void * arg)49 static 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
run(void)62 static 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
cleanup(void)116 static void cleanup(void)
117 {
118 tst_fzsync_pair_cleanup(&fzsync_pair);
119
120 if (sock >= 0)
121 SAFE_CLOSE(sock);
122 }
123
124 static 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