1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2018 Oracle and/or its affiliates. All Rights Reserved. 4 * Copyright (c) Linux Test Project, 2019-2023 5 */ 6 7/*\ 8 * [Description] 9 * 10 * Regression test for the crash caused by over-sized SCTP chunk, 11 * fixed by upstream commit 07f2c7ab6f8d ("sctp: verify size of a new 12 * chunk in _sctp_make_chunk()"). 13 */ 14 15#include <stdlib.h> 16#include <unistd.h> 17#include <sys/types.h> 18#include <sys/socket.h> 19#include <netinet/in.h> 20#include <netinet/ip.h> 21#include <netinet/ip6.h> 22#include <netdb.h> 23#include <sys/syscall.h> 24 25#include "tst_test.h" 26#include "tst_safe_stdio.h" 27#include "tst_checksum.h" 28#include "lapi/netinet_in.h" 29#include "lapi/socket.h" 30#include "lapi/sctp.h" 31 32static int port; 33static int sfd, cfd; 34static struct sockaddr_in6 rmt, loc; 35 36static uint8_t packet[IP_MAXPACKET]; 37static int pkt_len; 38static char *addr_param; 39static int addr_num = 3273; 40 41static void setup_server(void) 42{ 43 const char hmac_algo_path[] = "/proc/sys/net/sctp/cookie_hmac_alg"; 44 char hmac_algo[CHAR_MAX]; 45 int hmac_algo_changed = 0; 46 int fd; 47 48 /* Disable md5 if fips is enabled. Set it to none */ 49 if (tst_fips_enabled()) { 50 /* Load sctp module */ 51 if (access(hmac_algo_path, F_OK) < 0) { 52 fd = SAFE_SOCKET(PF_INET, SOCK_STREAM, IPPROTO_SCTP); 53 SAFE_CLOSE(fd); 54 } 55 56 if (!access(hmac_algo_path, F_OK)) { 57 SAFE_FILE_SCANF(hmac_algo_path, "%s", hmac_algo); 58 SAFE_FILE_PRINTF(hmac_algo_path, "%s", "none"); 59 hmac_algo_changed = 1; 60 } 61 } 62 63 loc.sin6_family = AF_INET6; 64 loc.sin6_addr = in6addr_loopback; 65 66 sfd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, IPPROTO_SCTP); 67 SAFE_BIND(sfd, (struct sockaddr *)&loc, sizeof(loc)); 68 69 port = TST_GETSOCKPORT(sfd); 70 tst_res(TINFO, "sctp server listen on %d", port); 71 72 SAFE_LISTEN(sfd, 1); 73 74 srand(port); 75 76 if (hmac_algo_changed) 77 SAFE_FILE_PRINTF(hmac_algo_path, "%s", hmac_algo); 78} 79 80static void update_packet_field(size_t *off, void *buf, size_t buf_len) 81{ 82 memcpy(packet + *off, buf, buf_len); 83 *off += buf_len; 84} 85 86static void setup_client(void) 87{ 88 struct ip6_hdr ip6; 89 const size_t ip6_hdr_len = sizeof(ip6); 90 size_t cmn_hdr_off; 91 size_t off; 92 int i; 93 94 memset(&ip6, 0, sizeof(ip6)); 95 ip6.ip6_flow = htonl(6 << 28 | 2 << 20); 96 ip6.ip6_hops = 64; 97 ip6.ip6_nxt = IPPROTO_SCTP; 98 ip6.ip6_src.s6_addr[15] = 1; 99 ip6.ip6_dst.s6_addr[15] = 1; 100 rmt.sin6_family = AF_INET6; 101 rmt.sin6_addr = in6addr_loopback; 102 103 /* SCTP common header */ 104 off = ip6_hdr_len; 105 106 uint16_t src_port = htons(port - 1); 107 uint16_t dst_port = htons(port); 108 uint32_t vtag = 0; 109 uint32_t checksum = 0; 110 111 update_packet_field(&off, &src_port, 2); 112 update_packet_field(&off, &dst_port, 2); 113 update_packet_field(&off, &vtag, 4); 114 update_packet_field(&off, &checksum, 4); 115 cmn_hdr_off = off; 116 117 /* SCTP INIT chunk */ 118 uint16_t chunk_len; 119 120 packet[off++] = 1; 121 packet[off++] = 0; 122 off += 2; /* chunk length, will be set in the end */ 123 124 uint32_t init_tag = rand(); 125 uint32_t rwnd = htonl(106496); 126 uint16_t outs = htons(10); 127 uint16_t ins = htons(65535); 128 uint32_t init_tsn = rand(); 129 130 update_packet_field(&off, &init_tag, 4); 131 update_packet_field(&off, &rwnd, 4); 132 update_packet_field(&off, &outs, 2); 133 update_packet_field(&off, &ins, 2); 134 update_packet_field(&off, &init_tsn, 4); 135 136 /* SCTP optional parameter for IPv6 addresses */ 137 uint16_t param_type = htons(6); 138 uint16_t param_len = htons(20); 139 140 /* IPv6(40) + SCTP_COMMON(12) + SCTP_CHUNK(20) + SCTP_OPT(65460)) */ 141 for (i = 0; i < addr_num; ++i) { 142 update_packet_field(&off, ¶m_type, 2); 143 update_packet_field(&off, ¶m_len, 2); 144 packet[off + 15] = 1; 145 off += 16; 146 } 147 pkt_len = off; 148 149 tst_res(TINFO, "set chunk length %zu", pkt_len - cmn_hdr_off); 150 chunk_len = htons(pkt_len - cmn_hdr_off); 151 memcpy(packet + cmn_hdr_off + 2, &chunk_len, 2); 152 153 /* set checksum for SCTP: common header + INIT chunk */ 154 uint32_t csum = tst_crc32c(packet + ip6_hdr_len, pkt_len - ip6_hdr_len); 155 156 memcpy(packet + ip6_hdr_len + 8, &csum, 4); 157 158 ip6.ip6_plen = htons(pkt_len - ip6_hdr_len); 159 memcpy(packet, &ip6, ip6_hdr_len); 160 161 cfd = SAFE_SOCKET(AF_INET6, SOCK_RAW, IPPROTO_RAW); 162} 163 164static const char mtu_path[] = "/sys/class/net/lo/mtu"; 165static const unsigned int max_mtu = 65535; 166static unsigned int mtu; 167 168static void setup(void) 169{ 170 if (tst_parse_int(addr_param, &addr_num, 1, INT_MAX)) 171 tst_brk(TBROK, "wrong address number '%s'", addr_param); 172 173 /* We don't fragment IPv6 packet here yet, check that MTU is 65535 */ 174 SAFE_FILE_SCANF(mtu_path, "%d", &mtu); 175 if (mtu < max_mtu) 176 tst_brk(TCONF, "Test needs that 'lo' MTU has %d", max_mtu); 177 178 setup_server(); 179 setup_client(); 180} 181 182static void run(void) 183{ 184 int pid = SAFE_FORK(); 185 186 if (!pid) { 187 struct sockaddr_in6 addr6; 188 socklen_t addr_size = sizeof(addr6); 189 190 if (accept(sfd, (struct sockaddr *)&addr6, &addr_size) < 0) 191 tst_brk(TBROK | TERRNO, "accept() failed"); 192 exit(0); 193 } 194 195 SAFE_SENDTO(1, cfd, packet, pkt_len, 0, (struct sockaddr *)&rmt, 196 sizeof(rmt)); 197 198 SAFE_KILL(pid, SIGKILL); 199 SAFE_WAITPID(pid, NULL, 0); 200 201 tst_res(TPASS, "test doesn't cause crash"); 202} 203 204static struct tst_test test = { 205 .needs_root = 1, 206 .setup = setup, 207 .forks_child = 1, 208 .test_all = run, 209 .options = (struct tst_option[]) { 210 {"a:", &addr_param, "Number of additional IP address params"}, 211 {} 212 }, 213 .tags = (const struct tst_tag[]) { 214 {"CVE", "2018-5803"}, 215 {"linux-git", "07f2c7ab6f8d"}, 216 {} 217 } 218}; 219