1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-or-later
2*49cdfc7eSAndroid Build Coastguard Worker /*
3*49cdfc7eSAndroid Build Coastguard Worker * Copyright (C) 2023 SUSE LLC Richard Palethorpe <[email protected]>
4*49cdfc7eSAndroid Build Coastguard Worker */
5*49cdfc7eSAndroid Build Coastguard Worker /*\
6*49cdfc7eSAndroid Build Coastguard Worker * [Description]
7*49cdfc7eSAndroid Build Coastguard Worker *
8*49cdfc7eSAndroid Build Coastguard Worker * Reproducer for CVE-2023-0461 which is an exploitable use-after-free
9*49cdfc7eSAndroid Build Coastguard Worker * in a TLS socket. In fact it is exploitable in any User Level
10*49cdfc7eSAndroid Build Coastguard Worker * Protocol (ULP) which does not clone its context when accepting a
11*49cdfc7eSAndroid Build Coastguard Worker * connection.
12*49cdfc7eSAndroid Build Coastguard Worker *
13*49cdfc7eSAndroid Build Coastguard Worker * Because it does not clone the context, the child socket which is
14*49cdfc7eSAndroid Build Coastguard Worker * created on accept has a pointer to the listening socket's
15*49cdfc7eSAndroid Build Coastguard Worker * context. When the child is closed the parent's context is freed
16*49cdfc7eSAndroid Build Coastguard Worker * while it still has a reference to it.
17*49cdfc7eSAndroid Build Coastguard Worker *
18*49cdfc7eSAndroid Build Coastguard Worker * TLS can only be added to a socket which is connected. Not listening
19*49cdfc7eSAndroid Build Coastguard Worker * or disconnected, and a connected socket can not be set to
20*49cdfc7eSAndroid Build Coastguard Worker * listening. So we have to connect the socket, add TLS, then
21*49cdfc7eSAndroid Build Coastguard Worker * disconnect, then set it to listening.
22*49cdfc7eSAndroid Build Coastguard Worker *
23*49cdfc7eSAndroid Build Coastguard Worker * To my knowledge, setting a socket from open to disconnected
24*49cdfc7eSAndroid Build Coastguard Worker * requires a trick; we have to "connect" to an unspecified
25*49cdfc7eSAndroid Build Coastguard Worker * address. This could explain why the bug was not found earlier.
26*49cdfc7eSAndroid Build Coastguard Worker *
27*49cdfc7eSAndroid Build Coastguard Worker * The accepted fix was to disallow listening on sockets with a ULP
28*49cdfc7eSAndroid Build Coastguard Worker * set which does not have a clone function.
29*49cdfc7eSAndroid Build Coastguard Worker *
30*49cdfc7eSAndroid Build Coastguard Worker * The test uses two processes, first the child acts as a server so
31*49cdfc7eSAndroid Build Coastguard Worker * that the parent can create the TLS socket. Then the child connects
32*49cdfc7eSAndroid Build Coastguard Worker * to the parent's TLS socket.
33*49cdfc7eSAndroid Build Coastguard Worker *
34*49cdfc7eSAndroid Build Coastguard Worker * When we try to listen on the parent, the current kernel should
35*49cdfc7eSAndroid Build Coastguard Worker * return EINVAL. However if clone is implemented then this could
36*49cdfc7eSAndroid Build Coastguard Worker * become a valid operation. It is also quite easy to crash the kernel
37*49cdfc7eSAndroid Build Coastguard Worker * if we set some TLS options before doing a double free.
38*49cdfc7eSAndroid Build Coastguard Worker *
39*49cdfc7eSAndroid Build Coastguard Worker * commit 2c02d41d71f90a5168391b6a5f2954112ba2307c
40*49cdfc7eSAndroid Build Coastguard Worker * Author: Paolo Abeni <[email protected]>
41*49cdfc7eSAndroid Build Coastguard Worker * Date: Tue Jan 3 12:19:17 2023 +0100
42*49cdfc7eSAndroid Build Coastguard Worker *
43*49cdfc7eSAndroid Build Coastguard Worker * net/ulp: prevent ULP without clone op from entering the LISTEN status
44*49cdfc7eSAndroid Build Coastguard Worker */
45*49cdfc7eSAndroid Build Coastguard Worker
46*49cdfc7eSAndroid Build Coastguard Worker #include "sched.h"
47*49cdfc7eSAndroid Build Coastguard Worker #include "tst_test.h"
48*49cdfc7eSAndroid Build Coastguard Worker
49*49cdfc7eSAndroid Build Coastguard Worker #ifdef HAVE_LINUX_TLS_H
50*49cdfc7eSAndroid Build Coastguard Worker
51*49cdfc7eSAndroid Build Coastguard Worker #include <linux/tls.h>
52*49cdfc7eSAndroid Build Coastguard Worker #include <netinet/in.h>
53*49cdfc7eSAndroid Build Coastguard Worker
54*49cdfc7eSAndroid Build Coastguard Worker #include "lapi/sched.h"
55*49cdfc7eSAndroid Build Coastguard Worker #include "lapi/socket.h"
56*49cdfc7eSAndroid Build Coastguard Worker #include "lapi/tcp.h"
57*49cdfc7eSAndroid Build Coastguard Worker #include "tst_checkpoint.h"
58*49cdfc7eSAndroid Build Coastguard Worker #include "tst_net.h"
59*49cdfc7eSAndroid Build Coastguard Worker #include "tst_safe_net.h"
60*49cdfc7eSAndroid Build Coastguard Worker #include "tst_taint.h"
61*49cdfc7eSAndroid Build Coastguard Worker
62*49cdfc7eSAndroid Build Coastguard Worker static struct tls12_crypto_info_aes_gcm_128 opts = {
63*49cdfc7eSAndroid Build Coastguard Worker .info = {
64*49cdfc7eSAndroid Build Coastguard Worker .version = TLS_1_2_VERSION,
65*49cdfc7eSAndroid Build Coastguard Worker .cipher_type = TLS_CIPHER_AES_GCM_128,
66*49cdfc7eSAndroid Build Coastguard Worker },
67*49cdfc7eSAndroid Build Coastguard Worker .iv = { 'i', 'v' },
68*49cdfc7eSAndroid Build Coastguard Worker .key = { 'k', 'e', 'y' },
69*49cdfc7eSAndroid Build Coastguard Worker .salt = { 's', 'a', 'l', 't' },
70*49cdfc7eSAndroid Build Coastguard Worker .rec_seq = { 'r', 'e', 'c', 's' },
71*49cdfc7eSAndroid Build Coastguard Worker };
72*49cdfc7eSAndroid Build Coastguard Worker
73*49cdfc7eSAndroid Build Coastguard Worker static struct sockaddr_in tcp0_addr, tcp1_addr;
74*49cdfc7eSAndroid Build Coastguard Worker static const struct sockaddr unspec_addr = {
75*49cdfc7eSAndroid Build Coastguard Worker .sa_family = AF_UNSPEC
76*49cdfc7eSAndroid Build Coastguard Worker };
77*49cdfc7eSAndroid Build Coastguard Worker
78*49cdfc7eSAndroid Build Coastguard Worker static int tcp0_sk, tcp1_sk, tcp2_sk, tcp3_sk;
79*49cdfc7eSAndroid Build Coastguard Worker
setup(void)80*49cdfc7eSAndroid Build Coastguard Worker static void setup(void)
81*49cdfc7eSAndroid Build Coastguard Worker {
82*49cdfc7eSAndroid Build Coastguard Worker tst_init_sockaddr_inet(&tcp0_addr, "127.0.0.1", 0x7c90);
83*49cdfc7eSAndroid Build Coastguard Worker tst_init_sockaddr_inet(&tcp1_addr, "127.0.0.1", 0x7c91);
84*49cdfc7eSAndroid Build Coastguard Worker }
85*49cdfc7eSAndroid Build Coastguard Worker
cleanup(void)86*49cdfc7eSAndroid Build Coastguard Worker static void cleanup(void)
87*49cdfc7eSAndroid Build Coastguard Worker {
88*49cdfc7eSAndroid Build Coastguard Worker if (tcp0_sk > 0)
89*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp0_sk);
90*49cdfc7eSAndroid Build Coastguard Worker if (tcp1_sk > 0)
91*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp1_sk);
92*49cdfc7eSAndroid Build Coastguard Worker if (tcp2_sk > 0)
93*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp2_sk);
94*49cdfc7eSAndroid Build Coastguard Worker if (tcp3_sk > 0)
95*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp3_sk);
96*49cdfc7eSAndroid Build Coastguard Worker }
97*49cdfc7eSAndroid Build Coastguard Worker
child(void)98*49cdfc7eSAndroid Build Coastguard Worker static void child(void)
99*49cdfc7eSAndroid Build Coastguard Worker {
100*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "child: Listen for tcp1 connection");
101*49cdfc7eSAndroid Build Coastguard Worker tcp0_sk = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0);
102*49cdfc7eSAndroid Build Coastguard Worker SAFE_BIND(tcp0_sk, (struct sockaddr *)&tcp0_addr, sizeof(tcp0_addr));
103*49cdfc7eSAndroid Build Coastguard Worker SAFE_LISTEN(tcp0_sk, 1);
104*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAKE(0);
105*49cdfc7eSAndroid Build Coastguard Worker
106*49cdfc7eSAndroid Build Coastguard Worker tcp3_sk = SAFE_ACCEPT(tcp0_sk, NULL, 0);
107*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAIT(1);
108*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp3_sk);
109*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp0_sk);
110*49cdfc7eSAndroid Build Coastguard Worker
111*49cdfc7eSAndroid Build Coastguard Worker tcp3_sk = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0);
112*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAIT(2);
113*49cdfc7eSAndroid Build Coastguard Worker
114*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "child: connect for tcp2 connection");
115*49cdfc7eSAndroid Build Coastguard Worker TEST(connect(tcp3_sk, (struct sockaddr *)&tcp1_addr, sizeof(tcp1_addr)));
116*49cdfc7eSAndroid Build Coastguard Worker
117*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET == -1) {
118*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO | TTERRNO, "child: could not connect to tcp1");
119*49cdfc7eSAndroid Build Coastguard Worker return;
120*49cdfc7eSAndroid Build Coastguard Worker }
121*49cdfc7eSAndroid Build Coastguard Worker
122*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAIT(3);
123*49cdfc7eSAndroid Build Coastguard Worker }
124*49cdfc7eSAndroid Build Coastguard Worker
run(void)125*49cdfc7eSAndroid Build Coastguard Worker static void run(void)
126*49cdfc7eSAndroid Build Coastguard Worker {
127*49cdfc7eSAndroid Build Coastguard Worker const pid_t child_pid = SAFE_FORK();
128*49cdfc7eSAndroid Build Coastguard Worker
129*49cdfc7eSAndroid Build Coastguard Worker if (child_pid == 0) {
130*49cdfc7eSAndroid Build Coastguard Worker child();
131*49cdfc7eSAndroid Build Coastguard Worker return;
132*49cdfc7eSAndroid Build Coastguard Worker }
133*49cdfc7eSAndroid Build Coastguard Worker
134*49cdfc7eSAndroid Build Coastguard Worker tcp1_sk = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0);
135*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAIT(0);
136*49cdfc7eSAndroid Build Coastguard Worker
137*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "parent: Connect for tcp0 connection");
138*49cdfc7eSAndroid Build Coastguard Worker SAFE_CONNECT(tcp1_sk, (struct sockaddr *)&tcp0_addr, sizeof(tcp0_addr));
139*49cdfc7eSAndroid Build Coastguard Worker TEST(setsockopt(tcp1_sk, SOL_TCP, TCP_ULP, "tls", 3));
140*49cdfc7eSAndroid Build Coastguard Worker
141*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET == -1 && TST_ERR == ENOENT)
142*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF | TTERRNO, "parent: setsockopt failed: The TLS module is probably not loaded");
143*49cdfc7eSAndroid Build Coastguard Worker else if (TST_RET == -1)
144*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK | TTERRNO, "parent: setsockopt failed");
145*49cdfc7eSAndroid Build Coastguard Worker
146*49cdfc7eSAndroid Build Coastguard Worker SAFE_SETSOCKOPT(tcp1_sk, SOL_TLS, TLS_TX, &opts, sizeof(opts));
147*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAKE(1);
148*49cdfc7eSAndroid Build Coastguard Worker
149*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "parent: Disconnect by setting unspec address");
150*49cdfc7eSAndroid Build Coastguard Worker SAFE_CONNECT(tcp1_sk, &unspec_addr, sizeof(unspec_addr));
151*49cdfc7eSAndroid Build Coastguard Worker SAFE_BIND(tcp1_sk, (struct sockaddr *)&tcp1_addr, sizeof(tcp1_addr));
152*49cdfc7eSAndroid Build Coastguard Worker
153*49cdfc7eSAndroid Build Coastguard Worker TEST(listen(tcp1_sk, 1));
154*49cdfc7eSAndroid Build Coastguard Worker
155*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET == -1) {
156*49cdfc7eSAndroid Build Coastguard Worker if (TST_ERR == EINVAL)
157*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS | TTERRNO, "parent: Can't listen on disconnected TLS socket");
158*49cdfc7eSAndroid Build Coastguard Worker else
159*49cdfc7eSAndroid Build Coastguard Worker tst_res(TCONF | TTERRNO, "parent: Can't listen on disconnected TLS socket, but the errno is not EINVAL as expected");
160*49cdfc7eSAndroid Build Coastguard Worker
161*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAKE(2);
162*49cdfc7eSAndroid Build Coastguard Worker tst_reap_children();
163*49cdfc7eSAndroid Build Coastguard Worker return;
164*49cdfc7eSAndroid Build Coastguard Worker }
165*49cdfc7eSAndroid Build Coastguard Worker
166*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "parent: Can listen on disconnected TLS socket");
167*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAKE(2);
168*49cdfc7eSAndroid Build Coastguard Worker
169*49cdfc7eSAndroid Build Coastguard Worker tcp2_sk = SAFE_ACCEPT(tcp1_sk, NULL, 0);
170*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp2_sk);
171*49cdfc7eSAndroid Build Coastguard Worker
172*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "parent: Attempting double free, because we set cipher options this should result in an crash");
173*49cdfc7eSAndroid Build Coastguard Worker tst_flush();
174*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(tcp1_sk);
175*49cdfc7eSAndroid Build Coastguard Worker
176*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAKE(3);
177*49cdfc7eSAndroid Build Coastguard Worker tst_reap_children();
178*49cdfc7eSAndroid Build Coastguard Worker sched_yield();
179*49cdfc7eSAndroid Build Coastguard Worker
180*49cdfc7eSAndroid Build Coastguard Worker tst_res(TCONF, "parent: We're still here, maybe the kernel can clone the TLS-ULP context now?");
181*49cdfc7eSAndroid Build Coastguard Worker }
182*49cdfc7eSAndroid Build Coastguard Worker
183*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
184*49cdfc7eSAndroid Build Coastguard Worker .setup = setup,
185*49cdfc7eSAndroid Build Coastguard Worker .cleanup = cleanup,
186*49cdfc7eSAndroid Build Coastguard Worker .test_all = run,
187*49cdfc7eSAndroid Build Coastguard Worker .forks_child = 1,
188*49cdfc7eSAndroid Build Coastguard Worker .needs_checkpoints = 1,
189*49cdfc7eSAndroid Build Coastguard Worker .taint_check = TST_TAINT_W | TST_TAINT_D,
190*49cdfc7eSAndroid Build Coastguard Worker .needs_kconfigs = (const char *[]) {
191*49cdfc7eSAndroid Build Coastguard Worker "CONFIG_TLS",
192*49cdfc7eSAndroid Build Coastguard Worker NULL
193*49cdfc7eSAndroid Build Coastguard Worker },
194*49cdfc7eSAndroid Build Coastguard Worker .tags = (const struct tst_tag[]) {
195*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "2c02d41d71f90"},
196*49cdfc7eSAndroid Build Coastguard Worker {"CVE", "2023-0461"},
197*49cdfc7eSAndroid Build Coastguard Worker {}
198*49cdfc7eSAndroid Build Coastguard Worker }
199*49cdfc7eSAndroid Build Coastguard Worker };
200*49cdfc7eSAndroid Build Coastguard Worker
201*49cdfc7eSAndroid Build Coastguard Worker #else
202*49cdfc7eSAndroid Build Coastguard Worker
203*49cdfc7eSAndroid Build Coastguard Worker TST_TEST_TCONF("linux/tls.h missing, we assume your system is too old");
204*49cdfc7eSAndroid Build Coastguard Worker
205*49cdfc7eSAndroid Build Coastguard Worker #endif
206