• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2020 SUSE LLC <mdoucha@suse.cz>
4  *
5  * CVE-2021-3609
6  *
7  * Test for race condition vulnerability in CAN BCM. Fixed in:
8  *
9  *  commit d5f9023fa61ee8b94f37a93f08e94b136cf1e463
10  *  Author: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
11  *  Date:   Sat Jun 19 13:18:13 2021 -0300
12  *
13  *  can: bcm: delay release of struct bcm_op after synchronize_rcu()
14  *
15  * The test is skipped when running in 32-bit compat mode. The kernel
16  * compatibility layer for CAN structures is not implemented at the
17  * time of writing.
18  */
19 
20 #include "config.h"
21 #include "tst_test.h"
22 
23 #ifdef HAVE_LINUX_CAN_H
24 
25 #include <linux/can.h>
26 #include <linux/can/bcm.h>
27 
28 #include "tst_netdevice.h"
29 #include "tst_fuzzy_sync.h"
30 
31 #define LTP_DEVICE "ltp_vcan0"
32 
33 struct test_payload {
34 	struct bcm_msg_head head;
35 	struct can_frame frame;
36 };
37 
38 static int sock1 = -1, sock2 = -1;
39 static struct tst_fzsync_pair fzsync_pair;
40 
setup(void)41 static void setup(void)
42 {
43 	struct sockaddr_can addr = { .can_family = AF_CAN };
44 
45 	/*
46 	 * Older kernels require explicit modprobe of vcan. Newer kernels
47 	 * will load the modules automatically and support CAN in network
48 	 * namespace which would eliminate the need for running the test
49 	 * with root privileges.
50 	 */
51 	tst_cmd((const char*[]){"modprobe", "vcan", NULL}, NULL, NULL, 0);
52 
53 	NETDEV_ADD_DEVICE(LTP_DEVICE, "vcan");
54 	NETDEV_SET_STATE(LTP_DEVICE, 1);
55 	addr.can_ifindex = NETDEV_INDEX_BY_NAME(LTP_DEVICE);
56 	addr.can_addr.tp.rx_id = 1;
57 	sock1 = SAFE_SOCKET(AF_CAN, SOCK_DGRAM, CAN_BCM);
58 	SAFE_CONNECT(sock1, (struct sockaddr *)&addr, sizeof(addr));
59 
60 	fzsync_pair.exec_loops = 100000;
61 	tst_fzsync_pair_init(&fzsync_pair);
62 }
63 
thread_run(void * arg)64 static void *thread_run(void *arg)
65 {
66 	struct test_payload data = {
67 		{
68 			.opcode = TX_SEND,
69 			.flags = RX_NO_AUTOTIMER,
70 			.count = -1,
71 			.nframes = 1
72 		},
73 		{0}
74 	};
75 	struct iovec iov = {
76 		.iov_base = &data,
77 		.iov_len = sizeof(data)
78 	};
79 	struct msghdr msg = {
80 		.msg_iov = &iov,
81 		.msg_iovlen = 1
82 	};
83 
84 	while (tst_fzsync_run_b(&fzsync_pair)) {
85 		tst_fzsync_start_race_b(&fzsync_pair);
86 		SAFE_SENDMSG(iov.iov_len, sock1, &msg, 0);
87 		tst_fzsync_end_race_b(&fzsync_pair);
88 	}
89 
90 	return arg;
91 }
92 
run(void)93 static void run(void)
94 {
95 	struct sockaddr_can addr = { .can_family = AF_CAN };
96 	struct bcm_msg_head data = {
97 		.opcode = RX_SETUP,
98 		.flags = RX_FILTER_ID | SETTIMER | STARTTIMER,
99 		.ival1.tv_sec = 1,
100 		.ival2.tv_sec = 1
101 	};
102 	struct iovec iov = {
103 		.iov_base = &data,
104 		.iov_len = sizeof(data)
105 	};
106 	struct msghdr msg = {
107 		.msg_iov = &iov,
108 		.msg_iovlen = 1,
109 	};
110 
111 	tst_fzsync_pair_reset(&fzsync_pair, thread_run);
112 
113 	while (tst_fzsync_run_a(&fzsync_pair)) {
114 		sock2 = SAFE_SOCKET(AF_CAN, SOCK_DGRAM, CAN_BCM);
115 		SAFE_CONNECT(sock2, (struct sockaddr *)&addr, sizeof(addr));
116 		SAFE_SENDMSG(iov.iov_len, sock2, &msg, 0);
117 		tst_fzsync_start_race_a(&fzsync_pair);
118 		SAFE_CLOSE(sock2);
119 		tst_fzsync_end_race_a(&fzsync_pair);
120 	}
121 
122 	tst_res(TPASS, "Nothing bad happened, probably");
123 }
124 
cleanup(void)125 static void cleanup(void)
126 {
127 	tst_fzsync_pair_cleanup(&fzsync_pair);
128 
129 	if (sock1 >= 0)
130 		SAFE_CLOSE(sock1);
131 
132 	if (sock2 >= 0)
133 		SAFE_CLOSE(sock2);
134 
135 	NETDEV_REMOVE_DEVICE(LTP_DEVICE);
136 }
137 
138 static struct tst_test test = {
139 	.test_all = run,
140 	.setup = setup,
141 	.cleanup = cleanup,
142 	.taint_check = TST_TAINT_W | TST_TAINT_D,
143 	.needs_root = 1,
144 	.skip_in_compat = 1,
145 	.needs_drivers = (const char *const[]) {
146 		"vcan",
147 		"can-bcm",
148 		NULL
149 	},
150 	.tags = (const struct tst_tag[]) {
151 		{"linux-git", "d5f9023fa61e"},
152 		{"CVE", "2021-3609"},
153 		{}
154 	}
155 };
156 
157 #else
158 
159 TST_TEST_TCONF("The test was built without <linux/can.h>");
160 
161 #endif /* HAVE_LINUX_CAN_H */
162