1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2020 SUSE 4f08c3bdfSopenharmony_ci */ 5f08c3bdfSopenharmony_ci 6f08c3bdfSopenharmony_ci/*\ 7f08c3bdfSopenharmony_ci * [Description] 8f08c3bdfSopenharmony_ci * 9f08c3bdfSopenharmony_ci * Test based on Syzkaller reproducer: 10f08c3bdfSopenharmony_ci * https://syzkaller.appspot.com/bug?id=680c24ff647dd7241998e19da52e8136d2fd3523 11f08c3bdfSopenharmony_ci * 12f08c3bdfSopenharmony_ci * The SLIP and SLCAN disciplines can have a race between write_wakeup and 13f08c3bdfSopenharmony_ci * close/hangup. This at least allows the netdev private data (tty->disc_data) 14f08c3bdfSopenharmony_ci * to be set to NULL or possibly freed while a transmit operation is being 15f08c3bdfSopenharmony_ci * added to a workqueue. 16f08c3bdfSopenharmony_ci * 17f08c3bdfSopenharmony_ci * If the race condition exists, then the most likely result of running this 18f08c3bdfSopenharmony_ci * is a null pointer dereference. 19f08c3bdfSopenharmony_ci * 20f08c3bdfSopenharmony_ci * Note that we must set the line discipline to "mouse" first which, for 21f08c3bdfSopenharmony_ci * whatever reason, results in tty_wakeup being called during the close 22f08c3bdfSopenharmony_ci * operation. 23f08c3bdfSopenharmony_ci * 24f08c3bdfSopenharmony_ci * We also test a selection of other line disciplines, but only SLIP and SLCAN 25f08c3bdfSopenharmony_ci * are known to have the problem. 26f08c3bdfSopenharmony_ci * 27f08c3bdfSopenharmony_ci * Fixed by commit from v5.5: 28f08c3bdfSopenharmony_ci * 0ace17d56824 ("can, slip: Protect tty->disc_data in write_wakeup and close with RCU") 29f08c3bdfSopenharmony_ci 30f08c3bdfSopenharmony_ci * This is also regression test for commit from v4.5-rc1: 31f08c3bdfSopenharmony_ci * dd42bf119714 ("tty: Prevent ldisc drivers from re-using stale tty fields") 32f08c3bdfSopenharmony_ci */ 33f08c3bdfSopenharmony_ci 34f08c3bdfSopenharmony_ci#define _GNU_SOURCE 35f08c3bdfSopenharmony_ci#include <stdlib.h> 36f08c3bdfSopenharmony_ci#include <stdio.h> 37f08c3bdfSopenharmony_ci#include <errno.h> 38f08c3bdfSopenharmony_ci#include <termios.h> 39f08c3bdfSopenharmony_ci#include "lapi/ioctl.h" 40f08c3bdfSopenharmony_ci 41f08c3bdfSopenharmony_ci#include "tst_test.h" 42f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h" 43f08c3bdfSopenharmony_ci#include "tst_fuzzy_sync.h" 44f08c3bdfSopenharmony_ci 45f08c3bdfSopenharmony_cistruct ldisc_info { 46f08c3bdfSopenharmony_ci int n; 47f08c3bdfSopenharmony_ci char *name; 48f08c3bdfSopenharmony_ci}; 49f08c3bdfSopenharmony_ci 50f08c3bdfSopenharmony_cistatic struct ldisc_info ldiscs[] = { 51f08c3bdfSopenharmony_ci {2, "mouse"}, 52f08c3bdfSopenharmony_ci 53f08c3bdfSopenharmony_ci {1, "SLIP"}, 54f08c3bdfSopenharmony_ci {3, "Async PPP"}, 55f08c3bdfSopenharmony_ci {5, "AX25/KISS"}, 56f08c3bdfSopenharmony_ci {13, "HDLC"}, 57f08c3bdfSopenharmony_ci {14, "Sync PPP"}, 58f08c3bdfSopenharmony_ci {17, "SLCAN"}, 59f08c3bdfSopenharmony_ci {18, "PPS"}, 60f08c3bdfSopenharmony_ci {20, "CAIF"}, 61f08c3bdfSopenharmony_ci {21, "GSM"} 62f08c3bdfSopenharmony_ci}; 63f08c3bdfSopenharmony_ci 64f08c3bdfSopenharmony_cistatic struct tst_fzsync_pair fzp; 65f08c3bdfSopenharmony_cistatic volatile int ptmx; 66f08c3bdfSopenharmony_ci 67f08c3bdfSopenharmony_cistatic void *hangup(void *unused) 68f08c3bdfSopenharmony_ci{ 69f08c3bdfSopenharmony_ci int i; 70f08c3bdfSopenharmony_ci 71f08c3bdfSopenharmony_ci while (tst_fzsync_run_b(&fzp)) { 72f08c3bdfSopenharmony_ci tst_fzsync_start_race_b(&fzp); 73f08c3bdfSopenharmony_ci for (i = 0; i < 10; i++) { 74f08c3bdfSopenharmony_ci if (tcflush(ptmx, TCIFLUSH)) 75f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "tcflush(ptmx, TCIFLUSH)"); 76f08c3bdfSopenharmony_ci } 77f08c3bdfSopenharmony_ci tst_fzsync_end_race_b(&fzp); 78f08c3bdfSopenharmony_ci } 79f08c3bdfSopenharmony_ci 80f08c3bdfSopenharmony_ci return unused; 81f08c3bdfSopenharmony_ci} 82f08c3bdfSopenharmony_ci 83f08c3bdfSopenharmony_cistatic int set_ldisc(int tty, struct ldisc_info *ldisc) 84f08c3bdfSopenharmony_ci{ 85f08c3bdfSopenharmony_ci TEST(ioctl(tty, TIOCSETD, &ldisc->n)); 86f08c3bdfSopenharmony_ci 87f08c3bdfSopenharmony_ci if (!TST_RET) 88f08c3bdfSopenharmony_ci return 0; 89f08c3bdfSopenharmony_ci 90f08c3bdfSopenharmony_ci if (TST_ERR == EINVAL) { 91f08c3bdfSopenharmony_ci tst_res(TCONF | TTERRNO, 92f08c3bdfSopenharmony_ci "You don't appear to have the %s TTY line discipline", 93f08c3bdfSopenharmony_ci ldisc->name); 94f08c3bdfSopenharmony_ci } else { 95f08c3bdfSopenharmony_ci tst_res(TFAIL | TTERRNO, 96f08c3bdfSopenharmony_ci "Failed to set the %s line discipline", ldisc->name); 97f08c3bdfSopenharmony_ci } 98f08c3bdfSopenharmony_ci 99f08c3bdfSopenharmony_ci return 1; 100f08c3bdfSopenharmony_ci} 101f08c3bdfSopenharmony_ci 102f08c3bdfSopenharmony_cistatic void do_test(unsigned int n) 103f08c3bdfSopenharmony_ci{ 104f08c3bdfSopenharmony_ci int pts; 105f08c3bdfSopenharmony_ci char pts_path[PATH_MAX]; 106f08c3bdfSopenharmony_ci struct ldisc_info *ldisc = &ldiscs[n + 1]; 107f08c3bdfSopenharmony_ci 108f08c3bdfSopenharmony_ci tst_res(TINFO, "Creating PTY with %s line discipline", ldisc->name); 109f08c3bdfSopenharmony_ci 110f08c3bdfSopenharmony_ci tst_fzsync_pair_reset(&fzp, hangup); 111f08c3bdfSopenharmony_ci while (tst_fzsync_run_a(&fzp)) { 112f08c3bdfSopenharmony_ci ptmx = SAFE_OPEN("/dev/ptmx", O_RDONLY); 113f08c3bdfSopenharmony_ci if (grantpt(ptmx)) 114f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "grantpt(ptmx)"); 115f08c3bdfSopenharmony_ci if (unlockpt(ptmx)) 116f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "unlockpt(ptmx)"); 117f08c3bdfSopenharmony_ci if (ptsname_r(ptmx, pts_path, sizeof(pts_path))) 118f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "ptsname_r(ptmx, ...)"); 119f08c3bdfSopenharmony_ci pts = SAFE_OPEN(pts_path, O_RDONLY); 120f08c3bdfSopenharmony_ci 121f08c3bdfSopenharmony_ci if (set_ldisc(pts, &ldiscs[0])) 122f08c3bdfSopenharmony_ci tst_brk(TCONF, "Need to set mouse discipline first"); 123f08c3bdfSopenharmony_ci if (set_ldisc(pts, ldisc)) 124f08c3bdfSopenharmony_ci return; 125f08c3bdfSopenharmony_ci 126f08c3bdfSopenharmony_ci tst_fzsync_start_race_a(&fzp); 127f08c3bdfSopenharmony_ci ioctl(pts, TIOCVHANGUP); 128f08c3bdfSopenharmony_ci tst_fzsync_end_race_a(&fzp); 129f08c3bdfSopenharmony_ci 130f08c3bdfSopenharmony_ci SAFE_CLOSE(pts); 131f08c3bdfSopenharmony_ci SAFE_CLOSE(ptmx); 132f08c3bdfSopenharmony_ci } 133f08c3bdfSopenharmony_ci 134f08c3bdfSopenharmony_ci tst_res(TPASS, "Did not crash with %s TTY discipline", ldisc->name); 135f08c3bdfSopenharmony_ci} 136f08c3bdfSopenharmony_ci 137f08c3bdfSopenharmony_cistatic void setup(void) 138f08c3bdfSopenharmony_ci{ 139f08c3bdfSopenharmony_ci fzp.min_samples = 20; 140f08c3bdfSopenharmony_ci 141f08c3bdfSopenharmony_ci tst_fzsync_pair_init(&fzp); 142f08c3bdfSopenharmony_ci} 143f08c3bdfSopenharmony_ci 144f08c3bdfSopenharmony_cistatic void cleanup(void) 145f08c3bdfSopenharmony_ci{ 146f08c3bdfSopenharmony_ci tst_fzsync_pair_cleanup(&fzp); 147f08c3bdfSopenharmony_ci} 148f08c3bdfSopenharmony_ci 149f08c3bdfSopenharmony_cistatic struct tst_test test = { 150f08c3bdfSopenharmony_ci .test = do_test, 151f08c3bdfSopenharmony_ci .tcnt = 9, 152f08c3bdfSopenharmony_ci .setup = setup, 153f08c3bdfSopenharmony_ci .cleanup = cleanup, 154f08c3bdfSopenharmony_ci .needs_root = 1, 155f08c3bdfSopenharmony_ci .max_runtime = 30, 156f08c3bdfSopenharmony_ci .needs_kconfigs = (const char *const[]){ 157f08c3bdfSopenharmony_ci "CONFIG_SERIO_SERPORT", 158f08c3bdfSopenharmony_ci NULL 159f08c3bdfSopenharmony_ci }, 160f08c3bdfSopenharmony_ci .tags = (const struct tst_tag[]) { 161f08c3bdfSopenharmony_ci {"linux-git", "0ace17d568241"}, 162f08c3bdfSopenharmony_ci {"CVE", "2020-14416"}, 163f08c3bdfSopenharmony_ci {"linux-git", "dd42bf1197144"}, 164f08c3bdfSopenharmony_ci {} 165f08c3bdfSopenharmony_ci } 166f08c3bdfSopenharmony_ci}; 167