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