1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2017 Christoph Paasch <cpaasch@apple.com>
4 * Copyright (C) 2020 SUSE LLC <mdoucha@suse.cz>
5 *
6 * CVE-2018-9568
7 *
8 * Test that connect() to AF_UNSPEC address correctly converts IPV6 socket
9 * to IPV4 listen socket when IPV6_ADDRFORM is set to AF_INET.
10 * Kernel memory corruption fixed in:
11 *
12 * commit 9d538fa60bad4f7b23193c89e843797a1cf71ef3
13 * Author: Christoph Paasch <cpaasch@apple.com>
14 * Date: Tue Sep 26 17:38:50 2017 -0700
15 *
16 * net: Set sk_prot_creator when cloning sockets to the right proto
17 *
18 *
19 * Note: This test also detects setsockopt(IP_ADDRFORM) breakage caused by
20 * kernel commit b6f6118901d1. This bug is unrelated to CVE-2018-9568.
21 * Fixed in:
22 *
23 * commit 82c9ae440857840c56e05d4fb1427ee032531346
24 * Author: John Haxby <john.haxby@oracle.com>
25 * Date: Sat Apr 18 16:30:49 2020 +0100
26 *
27 * ipv6: fix restrict IPV6_ADDRFORM operation
28 */
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <netinet/tcp.h>
34 #include <arpa/inet.h>
35
36 #include "tst_test.h"
37 #include "tst_net.h"
38
39 static int listenfd = -1, fd = -1, confd1 = -1, confd2 = -1, confd3 = -1;
40 static struct sockaddr_in6 bind_addr;
41 static struct sockaddr_in bind_addr4, client_addr;
42 static struct sockaddr reset_addr;
43
setup(void)44 static void setup(void)
45 {
46 socklen_t size = sizeof(bind_addr);
47
48 tst_init_sockaddr_inet6_bin(&bind_addr, &in6addr_any, 0);
49 tst_init_sockaddr_inet_bin(&bind_addr4, INADDR_ANY, 0);
50 memset(&reset_addr, 0, sizeof(reset_addr));
51 reset_addr.sa_family = AF_UNSPEC;
52
53 listenfd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
54 SAFE_BIND(listenfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
55 SAFE_LISTEN(listenfd, 5);
56 SAFE_GETSOCKNAME(listenfd, (struct sockaddr *)&bind_addr, &size);
57 tst_init_sockaddr_inet(&client_addr, "127.0.0.1",
58 htons(bind_addr.sin6_port));
59 }
60
cleanup(void)61 static void cleanup(void)
62 {
63 if (confd3 >= 0)
64 SAFE_CLOSE(confd3);
65
66 if (confd2 >= 0)
67 SAFE_CLOSE(confd2);
68
69 if (confd1 >= 0)
70 SAFE_CLOSE(confd1);
71
72 if (fd >= 0)
73 SAFE_CLOSE(fd);
74
75 if (listenfd >= 0)
76 SAFE_CLOSE(listenfd);
77 }
78
run(void)79 static void run(void)
80 {
81 int i, addrlen, optval = AF_INET;
82 struct sockaddr_storage client_addr2;
83
84 for (i = 0; i < 1000; i++) {
85 confd1 = SAFE_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP);
86 SAFE_CONNECT(confd1, (struct sockaddr *)&client_addr,
87 sizeof(client_addr));
88
89 fd = SAFE_ACCEPT(listenfd, NULL, NULL);
90 TEST(setsockopt(fd, SOL_IPV6, IPV6_ADDRFORM, &optval,
91 sizeof(optval)));
92
93 if (TST_RET == -1) {
94 tst_res(TFAIL | TTERRNO,
95 "setsockopt(IPV6_ADDRFORM) failed");
96 return;
97 }
98
99 if (TST_RET != 0)
100 tst_brk(TBROK | TTERRNO, "setsockopt(IPV6_ADDRFORM) "
101 "returned invalid value");
102
103 SAFE_CONNECT(fd, (struct sockaddr *)&reset_addr,
104 sizeof(reset_addr));
105 SAFE_BIND(fd, (struct sockaddr *)&bind_addr4,
106 sizeof(bind_addr4));
107 SAFE_LISTEN(fd, 5);
108
109 addrlen = tst_get_connect_address(fd, &client_addr2);
110 confd2 = SAFE_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP);
111 SAFE_CONNECT(confd2, (struct sockaddr *)&client_addr2, addrlen);
112 confd3 = SAFE_ACCEPT(fd, NULL, NULL);
113
114 SAFE_CLOSE(confd3);
115 SAFE_CLOSE(confd2);
116 SAFE_CLOSE(confd1);
117 SAFE_CLOSE(fd);
118
119 if (tst_taint_check()) {
120 tst_res(TFAIL, "Kernel is vulnerable");
121 return;
122 }
123 }
124
125 tst_res(TPASS, "Nothing bad happened, probably");
126 }
127
128 static struct tst_test test = {
129 .test_all = run,
130 .setup = setup,
131 .cleanup = cleanup,
132 .taint_check = TST_TAINT_W | TST_TAINT_D,
133 .tags = (const struct tst_tag[]) {
134 {"linux-git", "9d538fa60bad"},
135 {"linux-git", "82c9ae440857"},
136 {"CVE", "2018-9568"},
137 {}
138 }
139 };
140