|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | +/* |
| 3 | + * Copyright (C) 2017 Christoph Paasch <[email protected]> |
| 4 | + * Copyright (C) 2020 SUSE LLC <[email protected]> |
| 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 <[email protected]> |
| 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 | +#include <sys/types.h> |
| 20 | +#include <sys/socket.h> |
| 21 | +#include <netinet/in.h> |
| 22 | +#include <netinet/tcp.h> |
| 23 | +#include <arpa/inet.h> |
| 24 | + |
| 25 | +#include "tst_test.h" |
| 26 | +#include "tst_net.h" |
| 27 | +#include "tst_taint.h" |
| 28 | + |
| 29 | +static int listenfd; |
| 30 | +static struct sockaddr_in6 bind_addr; |
| 31 | +static struct sockaddr_in bind_addr4, client_addr; |
| 32 | +static struct sockaddr reset_addr; |
| 33 | + |
| 34 | +static void setup(void) |
| 35 | +{ |
| 36 | + socklen_t size = sizeof(bind_addr); |
| 37 | + |
| 38 | + tst_taint_init(TST_TAINT_W | TST_TAINT_D); |
| 39 | + |
| 40 | + tst_init_sockaddr_inet6_bin(&bind_addr, &in6addr_any, 0); |
| 41 | + tst_init_sockaddr_inet_bin(&bind_addr4, INADDR_ANY, 0); |
| 42 | + memset(&reset_addr, 0, sizeof(reset_addr)); |
| 43 | + reset_addr.sa_family = AF_UNSPEC; |
| 44 | + |
| 45 | + listenfd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, IPPROTO_TCP); |
| 46 | + SAFE_BIND(listenfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); |
| 47 | + SAFE_LISTEN(listenfd, 5); |
| 48 | + SAFE_GETSOCKNAME(listenfd, (struct sockaddr *)&bind_addr, &size); |
| 49 | + tst_init_sockaddr_inet(&client_addr, "127.0.0.1", |
| 50 | + htons(bind_addr.sin6_port)); |
| 51 | +} |
| 52 | + |
| 53 | +static void cleanup(void) |
| 54 | +{ |
| 55 | + if (listenfd > 0) |
| 56 | + SAFE_CLOSE(listenfd); |
| 57 | +} |
| 58 | + |
| 59 | +static void run(void) |
| 60 | +{ |
| 61 | + int i, addrlen, fd, confd1, confd2, confd3; |
| 62 | + struct sockaddr_storage client_addr2; |
| 63 | + |
| 64 | + for (i = 0; i < 1000; i++) { |
| 65 | + confd1 = SAFE_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
| 66 | + SAFE_CONNECT(confd1, (struct sockaddr *)&client_addr, |
| 67 | + sizeof(client_addr)); |
| 68 | + |
| 69 | + fd = SAFE_ACCEPT(listenfd, NULL, NULL); |
| 70 | + SAFE_SETSOCKOPT_INT(fd, SOL_IPV6, IPV6_ADDRFORM, AF_INET); |
| 71 | + SAFE_CONNECT(fd, (struct sockaddr *)&reset_addr, |
| 72 | + sizeof(reset_addr)); |
| 73 | + SAFE_BIND(fd, (struct sockaddr *)&bind_addr4, |
| 74 | + sizeof(bind_addr4)); |
| 75 | + SAFE_LISTEN(fd, 5); |
| 76 | + |
| 77 | + addrlen = tst_get_connect_address(fd, &client_addr2); |
| 78 | + confd2 = SAFE_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
| 79 | + SAFE_CONNECT(confd2, (struct sockaddr *)&client_addr2, addrlen); |
| 80 | + confd3 = SAFE_ACCEPT(fd, NULL, NULL); |
| 81 | + |
| 82 | + SAFE_CLOSE(confd3); |
| 83 | + SAFE_CLOSE(confd2); |
| 84 | + SAFE_CLOSE(confd1); |
| 85 | + SAFE_CLOSE(fd); |
| 86 | + |
| 87 | + if (tst_taint_check()) { |
| 88 | + tst_res(TFAIL, "Kernel is vulnerable"); |
| 89 | + return; |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + tst_res(TPASS, "Nothing bad happened, probably"); |
| 94 | +} |
| 95 | + |
| 96 | +static struct tst_test test = { |
| 97 | + .test_all = run, |
| 98 | + .setup = setup, |
| 99 | + .cleanup = cleanup, |
| 100 | + .tags = (const struct tst_tag[]) { |
| 101 | + {"linux-git", "9d538fa60bad"}, |
| 102 | + {"CVE", "2018-9568"}, |
| 103 | + {} |
| 104 | + } |
| 105 | +}; |
0 commit comments