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 #include "tst_taint.h"
39
40 static int listenfd = -1, fd = -1, confd1 = -1, confd2 = -1, confd3 = -1;
41 static struct sockaddr_in6 bind_addr;
42 static struct sockaddr_in bind_addr4, client_addr;
43 static struct sockaddr reset_addr;
44
setup(void)45 static void setup(void)
46 {
47 socklen_t size = sizeof(bind_addr);
48
49 tst_taint_init(TST_TAINT_W | TST_TAINT_D);
50
51 tst_init_sockaddr_inet6_bin(&bind_addr, &in6addr_any, 0);
52 tst_init_sockaddr_inet_bin(&bind_addr4, INADDR_ANY, 0);
53 memset(&reset_addr, 0, sizeof(reset_addr));
54 reset_addr.sa_family = AF_UNSPEC;
55
56 listenfd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
57 SAFE_BIND(listenfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
58 SAFE_LISTEN(listenfd, 5);
59 SAFE_GETSOCKNAME(listenfd, (struct sockaddr *)&bind_addr, &size);
60 tst_init_sockaddr_inet(&client_addr, "127.0.0.1",
61 htons(bind_addr.sin6_port));
62 }
63
cleanup(void)64 static void cleanup(void)
65 {
66 if (confd3 >= 0)
67 SAFE_CLOSE(confd3);
68
69 if (confd2 >= 0)
70 SAFE_CLOSE(confd2);
71
72 if (confd1 >= 0)
73 SAFE_CLOSE(confd1);
74
75 if (fd >= 0)
76 SAFE_CLOSE(fd);
77
78 if (listenfd >= 0)
79 SAFE_CLOSE(listenfd);
80 }
81
run(void)82 static void run(void)
83 {
84 int i, addrlen, optval = AF_INET;
85 struct sockaddr_storage client_addr2;
86
87 for (i = 0; i < 1000; i++) {
88 confd1 = SAFE_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP);
89 SAFE_CONNECT(confd1, (struct sockaddr *)&client_addr,
90 sizeof(client_addr));
91
92 fd = SAFE_ACCEPT(listenfd, NULL, NULL);
93 TEST(setsockopt(fd, SOL_IPV6, IPV6_ADDRFORM, &optval,
94 sizeof(optval)));
95
96 if (TST_RET == -1) {
97 tst_res(TFAIL | TTERRNO,
98 "setsockopt(IPV6_ADDRFORM) failed");
99 return;
100 }
101
102 if (TST_RET != 0)
103 tst_brk(TBROK | TTERRNO, "setsockopt(IPV6_ADDRFORM) "
104 "returned invalid value");
105
106 SAFE_CONNECT(fd, (struct sockaddr *)&reset_addr,
107 sizeof(reset_addr));
108 SAFE_BIND(fd, (struct sockaddr *)&bind_addr4,
109 sizeof(bind_addr4));
110 SAFE_LISTEN(fd, 5);
111
112 addrlen = tst_get_connect_address(fd, &client_addr2);
113 confd2 = SAFE_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP);
114 SAFE_CONNECT(confd2, (struct sockaddr *)&client_addr2, addrlen);
115 confd3 = SAFE_ACCEPT(fd, NULL, NULL);
116
117 SAFE_CLOSE(confd3);
118 SAFE_CLOSE(confd2);
119 SAFE_CLOSE(confd1);
120 SAFE_CLOSE(fd);
121
122 if (tst_taint_check()) {
123 tst_res(TFAIL, "Kernel is vulnerable");
124 return;
125 }
126 }
127
128 tst_res(TPASS, "Nothing bad happened, probably");
129 }
130
131 static struct tst_test test = {
132 .test_all = run,
133 .setup = setup,
134 .cleanup = cleanup,
135 .tags = (const struct tst_tag[]) {
136 {"linux-git", "9d538fa60bad"},
137 {"linux-git", "82c9ae440857"},
138 {"CVE", "2018-9568"},
139 {}
140 }
141 };
142