1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2020 SUSE 4f08c3bdfSopenharmony_ci * 5f08c3bdfSopenharmony_ci * Test transmitting data over a PTY/TTY line discipline and reading from the 6f08c3bdfSopenharmony_ci * virtual netdev created by the line discipline. Also hangup the PTY while 7f08c3bdfSopenharmony_ci * data is in flight to try to cause a race between the netdev being deleted 8f08c3bdfSopenharmony_ci * and the discipline receive function writing to the netdev. 9f08c3bdfSopenharmony_ci * 10f08c3bdfSopenharmony_ci * For SLCAN we check stack data is not leaked in the frame padding 11f08c3bdfSopenharmony_ci * (CVE-2020-11494). 12f08c3bdfSopenharmony_ci * 13f08c3bdfSopenharmony_ci * Test flow: 14f08c3bdfSopenharmony_ci * 1. Create PTY with ldisc X which creates netdev Y 15f08c3bdfSopenharmony_ci * 2. Open raw packet socket and bind to netdev Y 16f08c3bdfSopenharmony_ci * 3. Send data on ptmx and read packets from socket 17f08c3bdfSopenharmony_ci * 4. Hangup while transmission in progress 18f08c3bdfSopenharmony_ci * 19f08c3bdfSopenharmony_ci * Note that not all line disciplines call unthrottle when they are ready to 20f08c3bdfSopenharmony_ci * read more bytes. So it is possible to fill all the write buffers causing 21f08c3bdfSopenharmony_ci * write to block forever (because once write sleeps it needs unthrottle to 22f08c3bdfSopenharmony_ci * wake it). So we write with O_NONBLOCK. 23f08c3bdfSopenharmony_ci * 24f08c3bdfSopenharmony_ci * Also the max buffer size for PTYs is 8192, so even if the protocol MTU is 25f08c3bdfSopenharmony_ci * greater everything may still be processed in 8129 byte chunks. At least 26f08c3bdfSopenharmony_ci * until we are in the netdev code which can have a bigger buffer. Of course 27f08c3bdfSopenharmony_ci * the MTU still decides exactly where the packet delimiter goes, this just 28f08c3bdfSopenharmony_ci * concerns choosing the best packet size to cause a race. 29f08c3bdfSopenharmony_ci * 30f08c3bdfSopenharmony_ci * Note on line discipline encapsulation formats: 31f08c3bdfSopenharmony_ci * - For SLIP frames we just write the data followed by a delimiter char 32f08c3bdfSopenharmony_ci * - SLCAN we write some ASCII described in drivers/net/can/slcan.c which is 33f08c3bdfSopenharmony_ci * converted to the actual frame by the kernel 34f08c3bdfSopenharmony_ci */ 35f08c3bdfSopenharmony_ci 36f08c3bdfSopenharmony_ci#define _GNU_SOURCE 37f08c3bdfSopenharmony_ci#include "config.h" 38f08c3bdfSopenharmony_ci#include "tst_test.h" 39f08c3bdfSopenharmony_ci#include "tst_buffers.h" 40f08c3bdfSopenharmony_ci#include "lapi/tty.h" 41f08c3bdfSopenharmony_ci 42f08c3bdfSopenharmony_ci#if defined(HAVE_LINUX_IF_PACKET_H) && defined(HAVE_LINUX_IF_ETHER_H) 43f08c3bdfSopenharmony_ci 44f08c3bdfSopenharmony_ci#include <linux/if_packet.h> 45f08c3bdfSopenharmony_ci#include <linux/if_ether.h> 46f08c3bdfSopenharmony_ci#include <linux/tty.h> 47f08c3bdfSopenharmony_ci 48f08c3bdfSopenharmony_ci/* 49f08c3bdfSopenharmony_ci * define instead of including <linux/can.h> to support kernel headers 50f08c3bdfSopenharmony_ci * before change from v4.2-rc1 51f08c3bdfSopenharmony_ci * a2f11835994e ("can.h: make padding given by gcc explicit"). 52f08c3bdfSopenharmony_ci */ 53f08c3bdfSopenharmony_ci 54f08c3bdfSopenharmony_ci#define CAN_MTU (sizeof(struct can_frame)) 55f08c3bdfSopenharmony_ci#define CAN_MAX_DLEN 8 56f08c3bdfSopenharmony_ci 57f08c3bdfSopenharmony_citypedef uint32_t canid_t; 58f08c3bdfSopenharmony_ci 59f08c3bdfSopenharmony_cistruct can_frame { 60f08c3bdfSopenharmony_ci canid_t can_id; 61f08c3bdfSopenharmony_ci uint8_t can_dlc; 62f08c3bdfSopenharmony_ci uint8_t __pad; 63f08c3bdfSopenharmony_ci uint8_t __res0; 64f08c3bdfSopenharmony_ci uint8_t __res1; 65f08c3bdfSopenharmony_ci uint8_t data[CAN_MAX_DLEN] __attribute__((aligned(8))); 66f08c3bdfSopenharmony_ci}; 67f08c3bdfSopenharmony_ci 68f08c3bdfSopenharmony_ci#include <stddef.h> 69f08c3bdfSopenharmony_ci#include <stdlib.h> 70f08c3bdfSopenharmony_ci#include <stdio.h> 71f08c3bdfSopenharmony_ci#include <errno.h> 72f08c3bdfSopenharmony_ci#include <unistd.h> 73f08c3bdfSopenharmony_ci#include <termios.h> 74f08c3bdfSopenharmony_ci#include <sys/types.h> 75f08c3bdfSopenharmony_ci#include <sys/socket.h> 76f08c3bdfSopenharmony_ci#include <net/if.h> 77f08c3bdfSopenharmony_ci#include "lapi/ioctl.h" 78f08c3bdfSopenharmony_ci 79f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h" 80f08c3bdfSopenharmony_ci 81f08c3bdfSopenharmony_ci#define SLCAN_FRAME "t00185f5f5f5f5f5f5f5f\r" 82f08c3bdfSopenharmony_ci 83f08c3bdfSopenharmony_cistruct ldisc_info { 84f08c3bdfSopenharmony_ci int n; 85f08c3bdfSopenharmony_ci char *name; 86f08c3bdfSopenharmony_ci int mtu; 87f08c3bdfSopenharmony_ci}; 88f08c3bdfSopenharmony_ci 89f08c3bdfSopenharmony_cistatic struct ldisc_info ldiscs[] = { 90f08c3bdfSopenharmony_ci {N_SLIP, "N_SLIP", 8192}, 91f08c3bdfSopenharmony_ci {N_SLCAN, "N_SLCAN", CAN_MTU}, 92f08c3bdfSopenharmony_ci}; 93f08c3bdfSopenharmony_ci 94f08c3bdfSopenharmony_cistatic int ptmx = -1, pts = -1, sk = -1, mtu, no_check; 95f08c3bdfSopenharmony_ci 96f08c3bdfSopenharmony_cistatic int set_ldisc(int tty, const struct ldisc_info *ldisc) 97f08c3bdfSopenharmony_ci{ 98f08c3bdfSopenharmony_ci TEST(ioctl(tty, TIOCSETD, &ldisc->n)); 99f08c3bdfSopenharmony_ci 100f08c3bdfSopenharmony_ci if (!TST_RET) 101f08c3bdfSopenharmony_ci return 0; 102f08c3bdfSopenharmony_ci 103f08c3bdfSopenharmony_ci if (TST_ERR == EINVAL) { 104f08c3bdfSopenharmony_ci tst_res(TCONF | TTERRNO, 105f08c3bdfSopenharmony_ci "You don't appear to have the %s TTY line discipline", 106f08c3bdfSopenharmony_ci ldisc->name); 107f08c3bdfSopenharmony_ci } else { 108f08c3bdfSopenharmony_ci tst_res(TFAIL | TTERRNO, 109f08c3bdfSopenharmony_ci "Failed to set the %s line discipline", ldisc->name); 110f08c3bdfSopenharmony_ci } 111f08c3bdfSopenharmony_ci 112f08c3bdfSopenharmony_ci return 1; 113f08c3bdfSopenharmony_ci} 114f08c3bdfSopenharmony_ci 115f08c3bdfSopenharmony_cistatic int open_pty(const struct ldisc_info *ldisc) 116f08c3bdfSopenharmony_ci{ 117f08c3bdfSopenharmony_ci char pts_path[PATH_MAX]; 118f08c3bdfSopenharmony_ci 119f08c3bdfSopenharmony_ci ptmx = SAFE_OPEN("/dev/ptmx", O_RDWR); 120f08c3bdfSopenharmony_ci if (grantpt(ptmx)) 121f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "grantpt(ptmx)"); 122f08c3bdfSopenharmony_ci if (unlockpt(ptmx)) 123f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "unlockpt(ptmx)"); 124f08c3bdfSopenharmony_ci if (ptsname_r(ptmx, pts_path, sizeof(pts_path))) 125f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "ptsname_r(ptmx, ...)"); 126f08c3bdfSopenharmony_ci 127f08c3bdfSopenharmony_ci SAFE_FCNTL(ptmx, F_SETFL, O_NONBLOCK); 128f08c3bdfSopenharmony_ci 129f08c3bdfSopenharmony_ci tst_res(TINFO, "PTS path is %s", pts_path); 130f08c3bdfSopenharmony_ci pts = SAFE_OPEN(pts_path, O_RDWR); 131f08c3bdfSopenharmony_ci 132f08c3bdfSopenharmony_ci return set_ldisc(pts, ldisc); 133f08c3bdfSopenharmony_ci} 134f08c3bdfSopenharmony_ci 135f08c3bdfSopenharmony_cistatic ssize_t try_async_write(int fd, const char *data, ssize_t size, 136f08c3bdfSopenharmony_ci ssize_t *done) 137f08c3bdfSopenharmony_ci{ 138f08c3bdfSopenharmony_ci ssize_t off = done ? *done : 0; 139f08c3bdfSopenharmony_ci ssize_t ret = write(fd, data + off, size - off); 140f08c3bdfSopenharmony_ci 141f08c3bdfSopenharmony_ci if (ret < 0) 142f08c3bdfSopenharmony_ci return -(errno != EAGAIN); 143f08c3bdfSopenharmony_ci 144f08c3bdfSopenharmony_ci if (!done) 145f08c3bdfSopenharmony_ci return 1; 146f08c3bdfSopenharmony_ci 147f08c3bdfSopenharmony_ci *done += ret; 148f08c3bdfSopenharmony_ci return *done >= size; 149f08c3bdfSopenharmony_ci} 150f08c3bdfSopenharmony_ci 151f08c3bdfSopenharmony_cistatic ssize_t try_async_read(int fd, char *data, ssize_t size, 152f08c3bdfSopenharmony_ci ssize_t *done) 153f08c3bdfSopenharmony_ci{ 154f08c3bdfSopenharmony_ci ssize_t off = done ? *done : 0; 155f08c3bdfSopenharmony_ci ssize_t ret = read(fd, data + off, size - off); 156f08c3bdfSopenharmony_ci 157f08c3bdfSopenharmony_ci if (ret < 0) 158f08c3bdfSopenharmony_ci return -(errno != EAGAIN); 159f08c3bdfSopenharmony_ci 160f08c3bdfSopenharmony_ci if (!done) 161f08c3bdfSopenharmony_ci return 1; 162f08c3bdfSopenharmony_ci 163f08c3bdfSopenharmony_ci *done += ret; 164f08c3bdfSopenharmony_ci return *done >= size; 165f08c3bdfSopenharmony_ci} 166f08c3bdfSopenharmony_ci 167f08c3bdfSopenharmony_cistatic ssize_t retry_async_write(int fd, const char *data, ssize_t size) 168f08c3bdfSopenharmony_ci{ 169f08c3bdfSopenharmony_ci ssize_t done = 0; 170f08c3bdfSopenharmony_ci 171f08c3bdfSopenharmony_ci return TST_RETRY_FUNC(try_async_write(fd, data, size, &done), 172f08c3bdfSopenharmony_ci TST_RETVAL_NOTNULL); 173f08c3bdfSopenharmony_ci} 174f08c3bdfSopenharmony_ci 175f08c3bdfSopenharmony_cistatic ssize_t retry_async_read(int fd, char *data, ssize_t size) 176f08c3bdfSopenharmony_ci{ 177f08c3bdfSopenharmony_ci ssize_t done = 0; 178f08c3bdfSopenharmony_ci 179f08c3bdfSopenharmony_ci return TST_RETRY_FUNC(try_async_read(fd, data, size, &done), 180f08c3bdfSopenharmony_ci TST_RETVAL_NOTNULL); 181f08c3bdfSopenharmony_ci} 182f08c3bdfSopenharmony_ci 183f08c3bdfSopenharmony_cistatic void do_pty(const struct ldisc_info *ldisc) 184f08c3bdfSopenharmony_ci{ 185f08c3bdfSopenharmony_ci char *data; 186f08c3bdfSopenharmony_ci ssize_t ret; 187f08c3bdfSopenharmony_ci size_t len = 0; 188f08c3bdfSopenharmony_ci 189f08c3bdfSopenharmony_ci switch (ldisc->n) { 190f08c3bdfSopenharmony_ci case N_SLIP: 191f08c3bdfSopenharmony_ci len = mtu; 192f08c3bdfSopenharmony_ci break; 193f08c3bdfSopenharmony_ci case N_SLCAN: 194f08c3bdfSopenharmony_ci len = sizeof(SLCAN_FRAME) - 1; 195f08c3bdfSopenharmony_ci break; 196f08c3bdfSopenharmony_ci } 197f08c3bdfSopenharmony_ci 198f08c3bdfSopenharmony_ci data = tst_alloc(len); 199f08c3bdfSopenharmony_ci 200f08c3bdfSopenharmony_ci switch (ldisc->n) { 201f08c3bdfSopenharmony_ci case N_SLIP: 202f08c3bdfSopenharmony_ci memset(data, '_', len - 1); 203f08c3bdfSopenharmony_ci data[len - 1] = 0300; 204f08c3bdfSopenharmony_ci break; 205f08c3bdfSopenharmony_ci case N_SLCAN: 206f08c3bdfSopenharmony_ci memcpy(data, SLCAN_FRAME, len); 207f08c3bdfSopenharmony_ci break; 208f08c3bdfSopenharmony_ci } 209f08c3bdfSopenharmony_ci 210f08c3bdfSopenharmony_ci ret = retry_async_write(ptmx, data, len); 211f08c3bdfSopenharmony_ci if (ret < 0) 212f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "Failed 1st write to PTY"); 213f08c3bdfSopenharmony_ci tst_res(TPASS, "Wrote PTY %s %d (1)", ldisc->name, ptmx); 214f08c3bdfSopenharmony_ci 215f08c3bdfSopenharmony_ci ret = retry_async_write(ptmx, data, len); 216f08c3bdfSopenharmony_ci if (ret < 0) 217f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "Failed 2nd write to PTY"); 218f08c3bdfSopenharmony_ci 219f08c3bdfSopenharmony_ci if (tcflush(ptmx, TCIFLUSH)) 220f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "tcflush(ptmx, TCIFLUSH)"); 221f08c3bdfSopenharmony_ci 222f08c3bdfSopenharmony_ci tst_res(TPASS, "Wrote PTY %s %d (2)", ldisc->name, ptmx); 223f08c3bdfSopenharmony_ci 224f08c3bdfSopenharmony_ci ret = retry_async_read(ptmx, data, len); 225f08c3bdfSopenharmony_ci if (ret < 0) 226f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "Failed read of PTY"); 227f08c3bdfSopenharmony_ci 228f08c3bdfSopenharmony_ci tst_res(TPASS, "Read PTY %s %d", ldisc->name, ptmx); 229f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAKE(0); 230f08c3bdfSopenharmony_ci 231f08c3bdfSopenharmony_ci while (1) { 232f08c3bdfSopenharmony_ci if (retry_async_read(ptmx, data, len) < 0) 233f08c3bdfSopenharmony_ci break; 234f08c3bdfSopenharmony_ci 235f08c3bdfSopenharmony_ci if (retry_async_write(ptmx, data, len) < 0) 236f08c3bdfSopenharmony_ci break; 237f08c3bdfSopenharmony_ci } 238f08c3bdfSopenharmony_ci 239f08c3bdfSopenharmony_ci tst_res(TPASS, "Transmission on PTY interrupted by hangup"); 240f08c3bdfSopenharmony_ci 241f08c3bdfSopenharmony_ci tst_free_all(); 242f08c3bdfSopenharmony_ci} 243f08c3bdfSopenharmony_ci 244f08c3bdfSopenharmony_cistatic void open_netdev(const struct ldisc_info *ldisc) 245f08c3bdfSopenharmony_ci{ 246f08c3bdfSopenharmony_ci struct ifreq ifreq = { 0 }; 247f08c3bdfSopenharmony_ci struct sockaddr_ll lla = { 0 }; 248f08c3bdfSopenharmony_ci 249f08c3bdfSopenharmony_ci SAFE_IOCTL(pts, SIOCGIFNAME, ifreq.ifr_name); 250f08c3bdfSopenharmony_ci tst_res(TINFO, "Netdev is %s", ifreq.ifr_name); 251f08c3bdfSopenharmony_ci 252f08c3bdfSopenharmony_ci sk = SAFE_SOCKET(PF_PACKET, SOCK_RAW, 0); 253f08c3bdfSopenharmony_ci 254f08c3bdfSopenharmony_ci ifreq.ifr_mtu = ldisc->mtu; 255f08c3bdfSopenharmony_ci if (ioctl(sk, SIOCSIFMTU, &ifreq)) 256f08c3bdfSopenharmony_ci tst_res(TWARN | TERRNO, "Failed to set netdev MTU to maximum"); 257f08c3bdfSopenharmony_ci SAFE_IOCTL(sk, SIOCGIFMTU, &ifreq); 258f08c3bdfSopenharmony_ci mtu = ifreq.ifr_mtu; 259f08c3bdfSopenharmony_ci tst_res(TINFO, "Netdev MTU is %d (we set %d)", mtu, ldisc->mtu); 260f08c3bdfSopenharmony_ci 261f08c3bdfSopenharmony_ci SAFE_IOCTL(sk, SIOCGIFFLAGS, &ifreq); 262f08c3bdfSopenharmony_ci ifreq.ifr_flags |= IFF_UP | IFF_RUNNING; 263f08c3bdfSopenharmony_ci SAFE_IOCTL(sk, SIOCSIFFLAGS, &ifreq); 264f08c3bdfSopenharmony_ci SAFE_IOCTL(sk, SIOCGIFFLAGS, &ifreq); 265f08c3bdfSopenharmony_ci 266f08c3bdfSopenharmony_ci if (!(ifreq.ifr_flags & IFF_UP)) 267f08c3bdfSopenharmony_ci tst_brk(TBROK, "Netdev did not come up"); 268f08c3bdfSopenharmony_ci 269f08c3bdfSopenharmony_ci SAFE_IOCTL(sk, SIOCGIFINDEX, &ifreq); 270f08c3bdfSopenharmony_ci 271f08c3bdfSopenharmony_ci lla.sll_family = PF_PACKET; 272f08c3bdfSopenharmony_ci lla.sll_protocol = htons(ETH_P_ALL); 273f08c3bdfSopenharmony_ci lla.sll_ifindex = ifreq.ifr_ifindex; 274f08c3bdfSopenharmony_ci SAFE_BIND(sk, (struct sockaddr *)&lla, sizeof(struct sockaddr_ll)); 275f08c3bdfSopenharmony_ci 276f08c3bdfSopenharmony_ci tst_res(TINFO, "Bound netdev %d to socket %d", ifreq.ifr_ifindex, sk); 277f08c3bdfSopenharmony_ci} 278f08c3bdfSopenharmony_ci 279f08c3bdfSopenharmony_cistatic void check_data(const struct ldisc_info *ldisc, 280f08c3bdfSopenharmony_ci const char *data, ssize_t len) 281f08c3bdfSopenharmony_ci{ 282f08c3bdfSopenharmony_ci ssize_t i = 0, j; 283f08c3bdfSopenharmony_ci struct can_frame frm; 284f08c3bdfSopenharmony_ci 285f08c3bdfSopenharmony_ci if (no_check) 286f08c3bdfSopenharmony_ci return; 287f08c3bdfSopenharmony_ci 288f08c3bdfSopenharmony_ci if (ldisc->n == N_SLCAN) { 289f08c3bdfSopenharmony_ci memcpy(&frm, data, len); 290f08c3bdfSopenharmony_ci 291f08c3bdfSopenharmony_ci if (frm.can_id != 1) { 292f08c3bdfSopenharmony_ci tst_res(TFAIL, "can_id = %d != 1", 293f08c3bdfSopenharmony_ci frm.can_id); 294f08c3bdfSopenharmony_ci no_check = 1; 295f08c3bdfSopenharmony_ci } 296f08c3bdfSopenharmony_ci 297f08c3bdfSopenharmony_ci if (frm.can_dlc != CAN_MAX_DLEN) { 298f08c3bdfSopenharmony_ci tst_res(TFAIL, "can_dlc = %d != " TST_TO_STR_(CAN_MAX_DLEN), 299f08c3bdfSopenharmony_ci frm.can_dlc); 300f08c3bdfSopenharmony_ci no_check = 1; 301f08c3bdfSopenharmony_ci } 302f08c3bdfSopenharmony_ci 303f08c3bdfSopenharmony_ci i = offsetof(struct can_frame, __pad); 304f08c3bdfSopenharmony_ci if (frm.__pad != frm.__res0 || frm.__res0 != frm.__res1) { 305f08c3bdfSopenharmony_ci tst_res_hexd(TFAIL, data + i, 306f08c3bdfSopenharmony_ci offsetof(struct can_frame, data) - i, 307f08c3bdfSopenharmony_ci "Padding bytes may contain stack data"); 308f08c3bdfSopenharmony_ci no_check = 1; 309f08c3bdfSopenharmony_ci } 310f08c3bdfSopenharmony_ci 311f08c3bdfSopenharmony_ci i = offsetof(struct can_frame, data); 312f08c3bdfSopenharmony_ci } 313f08c3bdfSopenharmony_ci 314f08c3bdfSopenharmony_ci do { 315f08c3bdfSopenharmony_ci if (i >= len) 316f08c3bdfSopenharmony_ci return; 317f08c3bdfSopenharmony_ci } while (data[i++] == '_'); 318f08c3bdfSopenharmony_ci 319f08c3bdfSopenharmony_ci j = i--; 320f08c3bdfSopenharmony_ci 321f08c3bdfSopenharmony_ci while (j < len && j - i < 65 && data[j++] != '_') 322f08c3bdfSopenharmony_ci ; 323f08c3bdfSopenharmony_ci j--; 324f08c3bdfSopenharmony_ci 325f08c3bdfSopenharmony_ci tst_res_hexd(TFAIL, data + i, j - i, 326f08c3bdfSopenharmony_ci "Corrupt data (max 64 of %ld bytes shown): data[%ld..%ld] = ", 327f08c3bdfSopenharmony_ci len, i, j); 328f08c3bdfSopenharmony_ci no_check = 1; 329f08c3bdfSopenharmony_ci 330f08c3bdfSopenharmony_ci if (no_check) 331f08c3bdfSopenharmony_ci tst_res(TINFO, "Will continue test without data checking"); 332f08c3bdfSopenharmony_ci} 333f08c3bdfSopenharmony_ci 334f08c3bdfSopenharmony_cistatic ssize_t try_sync_read(int fd, char *data, ssize_t size) 335f08c3bdfSopenharmony_ci{ 336f08c3bdfSopenharmony_ci ssize_t ret, n = 0; 337f08c3bdfSopenharmony_ci int retry = mtu; 338f08c3bdfSopenharmony_ci 339f08c3bdfSopenharmony_ci while (retry--) { 340f08c3bdfSopenharmony_ci ret = read(fd, data + n, size - n); 341f08c3bdfSopenharmony_ci 342f08c3bdfSopenharmony_ci if (ret < 0) 343f08c3bdfSopenharmony_ci return ret; 344f08c3bdfSopenharmony_ci 345f08c3bdfSopenharmony_ci if ((n += ret) >= size) 346f08c3bdfSopenharmony_ci return ret; 347f08c3bdfSopenharmony_ci } 348f08c3bdfSopenharmony_ci 349f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "Only read %zd of %zd bytes", n, size); 350f08c3bdfSopenharmony_ci 351f08c3bdfSopenharmony_ci return n; 352f08c3bdfSopenharmony_ci} 353f08c3bdfSopenharmony_ci 354f08c3bdfSopenharmony_cistatic ssize_t try_sync_write(int fd, const char *data, ssize_t size) 355f08c3bdfSopenharmony_ci{ 356f08c3bdfSopenharmony_ci ssize_t ret, n = 0; 357f08c3bdfSopenharmony_ci int retry = mtu; 358f08c3bdfSopenharmony_ci 359f08c3bdfSopenharmony_ci while (retry--) { 360f08c3bdfSopenharmony_ci ret = write(fd, data + n, size - n); 361f08c3bdfSopenharmony_ci 362f08c3bdfSopenharmony_ci if (ret < 0) 363f08c3bdfSopenharmony_ci return ret; 364f08c3bdfSopenharmony_ci 365f08c3bdfSopenharmony_ci if ((n += ret) >= size) 366f08c3bdfSopenharmony_ci return ret; 367f08c3bdfSopenharmony_ci } 368f08c3bdfSopenharmony_ci 369f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "Only wrote %zd of %zd bytes", n, size); 370f08c3bdfSopenharmony_ci 371f08c3bdfSopenharmony_ci return n; 372f08c3bdfSopenharmony_ci} 373f08c3bdfSopenharmony_ci 374f08c3bdfSopenharmony_cistatic void read_netdev(const struct ldisc_info *ldisc) 375f08c3bdfSopenharmony_ci{ 376f08c3bdfSopenharmony_ci int rlen, plen = 0; 377f08c3bdfSopenharmony_ci char *data; 378f08c3bdfSopenharmony_ci 379f08c3bdfSopenharmony_ci switch (ldisc->n) { 380f08c3bdfSopenharmony_ci case N_SLIP: 381f08c3bdfSopenharmony_ci plen = mtu - 1; 382f08c3bdfSopenharmony_ci break; 383f08c3bdfSopenharmony_ci case N_SLCAN: 384f08c3bdfSopenharmony_ci plen = CAN_MTU; 385f08c3bdfSopenharmony_ci break; 386f08c3bdfSopenharmony_ci } 387f08c3bdfSopenharmony_ci data = tst_alloc(plen); 388f08c3bdfSopenharmony_ci 389f08c3bdfSopenharmony_ci tst_res(TINFO, "Reading from socket %d", sk); 390f08c3bdfSopenharmony_ci 391f08c3bdfSopenharmony_ci TEST(try_sync_read(sk, data, plen)); 392f08c3bdfSopenharmony_ci if (TST_RET < 0) 393f08c3bdfSopenharmony_ci tst_brk(TBROK | TTERRNO, "Read netdev %s %d (1)", ldisc->name, sk); 394f08c3bdfSopenharmony_ci check_data(ldisc, data, plen); 395f08c3bdfSopenharmony_ci tst_res(TPASS, "Read netdev %s %d (1)", ldisc->name, sk); 396f08c3bdfSopenharmony_ci 397f08c3bdfSopenharmony_ci TEST(try_sync_read(sk, data, plen)); 398f08c3bdfSopenharmony_ci if (TST_RET < 0) 399f08c3bdfSopenharmony_ci tst_brk(TBROK | TTERRNO, "Read netdev %s %d (2)", ldisc->name, sk); 400f08c3bdfSopenharmony_ci check_data(ldisc, data, plen); 401f08c3bdfSopenharmony_ci tst_res(TPASS, "Read netdev %s %d (2)", ldisc->name, sk); 402f08c3bdfSopenharmony_ci 403f08c3bdfSopenharmony_ci TEST(try_sync_write(sk, data, plen)); 404f08c3bdfSopenharmony_ci if (TST_RET < 0) 405f08c3bdfSopenharmony_ci tst_brk(TBROK | TTERRNO, "Write netdev %s %d", ldisc->name, sk); 406f08c3bdfSopenharmony_ci 407f08c3bdfSopenharmony_ci tst_res(TPASS, "Write netdev %s %d", ldisc->name, sk); 408f08c3bdfSopenharmony_ci 409f08c3bdfSopenharmony_ci while (1) { 410f08c3bdfSopenharmony_ci if (try_sync_write(sk, data, plen) < 0) 411f08c3bdfSopenharmony_ci break; 412f08c3bdfSopenharmony_ci 413f08c3bdfSopenharmony_ci if ((rlen = try_sync_read(sk, data, plen)) < 0) 414f08c3bdfSopenharmony_ci break; 415f08c3bdfSopenharmony_ci check_data(ldisc, data, rlen); 416f08c3bdfSopenharmony_ci } 417f08c3bdfSopenharmony_ci 418f08c3bdfSopenharmony_ci tst_res(TPASS, "Data transmission on netdev interrupted by hangup"); 419f08c3bdfSopenharmony_ci 420f08c3bdfSopenharmony_ci close(sk); 421f08c3bdfSopenharmony_ci tst_free_all(); 422f08c3bdfSopenharmony_ci} 423f08c3bdfSopenharmony_ci 424f08c3bdfSopenharmony_cistatic void do_test(unsigned int n) 425f08c3bdfSopenharmony_ci{ 426f08c3bdfSopenharmony_ci struct ldisc_info *ldisc = &ldiscs[n]; 427f08c3bdfSopenharmony_ci 428f08c3bdfSopenharmony_ci if (open_pty(ldisc)) 429f08c3bdfSopenharmony_ci return; 430f08c3bdfSopenharmony_ci 431f08c3bdfSopenharmony_ci open_netdev(ldisc); 432f08c3bdfSopenharmony_ci 433f08c3bdfSopenharmony_ci if (!SAFE_FORK()) { 434f08c3bdfSopenharmony_ci read_netdev(ldisc); 435f08c3bdfSopenharmony_ci return; 436f08c3bdfSopenharmony_ci } 437f08c3bdfSopenharmony_ci 438f08c3bdfSopenharmony_ci if (!SAFE_FORK()) { 439f08c3bdfSopenharmony_ci do_pty(ldisc); 440f08c3bdfSopenharmony_ci return; 441f08c3bdfSopenharmony_ci } 442f08c3bdfSopenharmony_ci 443f08c3bdfSopenharmony_ci if (!SAFE_FORK()) { 444f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAIT2(0, 100000); 445f08c3bdfSopenharmony_ci SAFE_IOCTL(pts, TIOCVHANGUP); 446f08c3bdfSopenharmony_ci tst_res(TINFO, "Sent hangup ioctl to PTS"); 447f08c3bdfSopenharmony_ci SAFE_IOCTL(ptmx, TIOCVHANGUP); 448f08c3bdfSopenharmony_ci tst_res(TINFO, "Sent hangup ioctl to PTM"); 449f08c3bdfSopenharmony_ci return; 450f08c3bdfSopenharmony_ci } 451f08c3bdfSopenharmony_ci 452f08c3bdfSopenharmony_ci tst_reap_children(); 453f08c3bdfSopenharmony_ci} 454f08c3bdfSopenharmony_ci 455f08c3bdfSopenharmony_cistatic void cleanup(void) 456f08c3bdfSopenharmony_ci{ 457f08c3bdfSopenharmony_ci if (pts >= 0) 458f08c3bdfSopenharmony_ci ioctl(pts, TIOCVHANGUP); 459f08c3bdfSopenharmony_ci 460f08c3bdfSopenharmony_ci if (ptmx >= 0) 461f08c3bdfSopenharmony_ci ioctl(ptmx, TIOCVHANGUP); 462f08c3bdfSopenharmony_ci 463f08c3bdfSopenharmony_ci if (sk >= 0) 464f08c3bdfSopenharmony_ci close(sk); 465f08c3bdfSopenharmony_ci 466f08c3bdfSopenharmony_ci tst_reap_children(); 467f08c3bdfSopenharmony_ci} 468f08c3bdfSopenharmony_ci 469f08c3bdfSopenharmony_cistatic struct tst_test test = { 470f08c3bdfSopenharmony_ci .test = do_test, 471f08c3bdfSopenharmony_ci .cleanup = cleanup, 472f08c3bdfSopenharmony_ci .tcnt = 2, 473f08c3bdfSopenharmony_ci .forks_child = 1, 474f08c3bdfSopenharmony_ci .needs_checkpoints = 1, 475f08c3bdfSopenharmony_ci .needs_root = 1, 476f08c3bdfSopenharmony_ci .tags = (const struct tst_tag[]){ 477f08c3bdfSopenharmony_ci {"linux-git", "b9258a2cece4ec1f020715fe3554bc2e360f6264"}, 478f08c3bdfSopenharmony_ci {"CVE", "CVE-2020-11494"}, 479f08c3bdfSopenharmony_ci {} 480f08c3bdfSopenharmony_ci } 481f08c3bdfSopenharmony_ci}; 482f08c3bdfSopenharmony_ci 483f08c3bdfSopenharmony_ci#else 484f08c3bdfSopenharmony_ci 485f08c3bdfSopenharmony_ciTST_TEST_TCONF("Need <linux/if_packet.h> and <linux/if_ether.h>"); 486f08c3bdfSopenharmony_ci 487f08c3bdfSopenharmony_ci#endif 488