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 39static int listenfd = -1, fd = -1, confd1 = -1, confd2 = -1, confd3 = -1; 40static struct sockaddr_in6 bind_addr; 41static struct sockaddr_in bind_addr4, client_addr; 42static struct sockaddr reset_addr; 43 44static 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 61static 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 79static 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 128static 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