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 #define _GNU_SOURCE
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sched.h>
27
28 #include "tst_test.h"
29 #include "tst_fuzzy_sync.h"
30 #include "lapi/if_packet.h"
31 #include "lapi/if_ether.h"
32
33 static int sock = -1;
34 static struct tst_fzsync_pair fzsync_pair;
35
setup(void)36 static void setup(void)
37 {
38 int real_uid = getuid();
39 int real_gid = getgid();
40
41 SAFE_TRY_FILE_PRINTF("/proc/sys/user/max_user_namespaces", "%d", 10);
42
43 SAFE_UNSHARE(CLONE_NEWUSER);
44 SAFE_UNSHARE(CLONE_NEWNET);
45 SAFE_FILE_PRINTF("/proc/self/setgroups", "deny");
46 SAFE_FILE_PRINTF("/proc/self/uid_map", "0 %d 1", real_uid);
47 SAFE_FILE_PRINTF("/proc/self/gid_map", "0 %d 1", real_gid);
48
49 /*
50 * Reproducing the bug on unpatched system takes <15 loops. The test
51 * is slow and the bug is mostly harmless so don't waste too much
52 * time.
53 */
54 fzsync_pair.exec_loops = 500;
55 tst_fzsync_pair_init(&fzsync_pair);
56 }
57
thread_run(void * arg)58 static void *thread_run(void *arg)
59 {
60 unsigned int val = 1 << 30;
61
62 while (tst_fzsync_run_b(&fzsync_pair)) {
63 tst_fzsync_start_race_b(&fzsync_pair);
64 setsockopt(sock, SOL_PACKET, PACKET_RESERVE, &val, sizeof(val));
65 tst_fzsync_end_race_b(&fzsync_pair);
66 }
67
68 return arg;
69 }
70
run(void)71 static void run(void)
72 {
73 unsigned int val, version = TPACKET_V3;
74 socklen_t vsize = sizeof(val);
75 struct tpacket_req3 req = {
76 .tp_block_size = 4096,
77 .tp_block_nr = 1,
78 .tp_frame_size = 4096,
79 .tp_frame_nr = 1,
80 .tp_retire_blk_tov = 100
81 };
82
83 tst_fzsync_pair_reset(&fzsync_pair, thread_run);
84
85 while (tst_fzsync_run_a(&fzsync_pair)) {
86 sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
87 TEST(setsockopt(sock, SOL_PACKET, PACKET_VERSION, &version,
88 sizeof(version)));
89
90 if (TST_RET == -1 && TST_ERR == EINVAL)
91 tst_brk(TCONF | TTERRNO, "TPACKET_V3 not supported");
92
93 if (TST_RET) {
94 tst_brk(TBROK | TTERRNO,
95 "setsockopt(PACKET_VERSION, TPACKET_V3");
96 }
97
98 tst_fzsync_start_race_a(&fzsync_pair);
99 TEST(setsockopt(sock, SOL_PACKET, PACKET_RX_RING, &req,
100 sizeof(req)));
101 tst_fzsync_end_race_a(&fzsync_pair);
102
103 SAFE_GETSOCKOPT(sock, SOL_PACKET, PACKET_RESERVE, &val, &vsize);
104 SAFE_CLOSE(sock);
105
106 if (TST_RET == -1 && TST_ERR == EINVAL) {
107 tst_fzsync_pair_add_bias(&fzsync_pair, 1);
108 continue;
109 }
110
111 if (TST_RET) {
112 tst_brk(TBROK | TTERRNO,
113 "Invalid setsockopt() return value");
114 }
115
116 if (val > req.tp_block_size){
117 tst_res(TFAIL, "PACKET_RESERVE checks bypassed");
118 return;
119 }
120 }
121
122 tst_res(TPASS, "Cannot reproduce bug");
123 }
124
cleanup(void)125 static void cleanup(void)
126 {
127 tst_fzsync_pair_cleanup(&fzsync_pair);
128
129 if (sock >= 0)
130 SAFE_CLOSE(sock);
131 }
132
133 static struct tst_test test = {
134 .test_all = run,
135 .setup = setup,
136 .cleanup = cleanup,
137 .needs_kconfigs = (const char *[]) {
138 "CONFIG_USER_NS=y",
139 "CONFIG_NET_NS=y",
140 NULL
141 },
142 .save_restore = (const char * const[]) {
143 "?/proc/sys/user/max_user_namespaces",
144 NULL,
145 },
146 .tags = (const struct tst_tag[]) {
147 {"linux-git", "c27927e372f0"},
148 {"CVE", "2017-1000111"},
149 {}
150 }
151 };
152