Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
ltp/testcases/kernel/pty/pty03.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
166 lines (140 sloc)
3.64 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0-or-later | |
/* | |
* Copyright (c) 2020 SUSE | |
*/ | |
/*\ | |
* [Description] | |
* | |
* Test based on Syzkaller reproducer: | |
* https://syzkaller.appspot.com/bug?id=680c24ff647dd7241998e19da52e8136d2fd3523 | |
* | |
* The SLIP and SLCAN disciplines can have a race between write_wakeup and | |
* close/hangup. This at least allows the netdev private data (tty->disc_data) | |
* to be set to NULL or possibly freed while a transmit operation is being | |
* added to a workqueue. | |
* | |
* If the race condition exists, then the most likely result of running this | |
* is a null pointer dereference. | |
* | |
* Note that we must set the line discipline to "mouse" first which, for | |
* whatever reason, results in tty_wakeup being called during the close | |
* operation. | |
* | |
* We also test a selection of other line disciplines, but only SLIP and SLCAN | |
* are known to have the problem. | |
* | |
* Fixed by commit from v5.5: | |
* 0ace17d56824 ("can, slip: Protect tty->disc_data in write_wakeup and close with RCU") | |
* This is also regression test for commit from v4.5-rc1: | |
* dd42bf119714 ("tty: Prevent ldisc drivers from re-using stale tty fields") | |
*/ | |
#define _GNU_SOURCE | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <termios.h> | |
#include "lapi/ioctl.h" | |
#include "tst_test.h" | |
#include "tst_safe_stdio.h" | |
#include "tst_fuzzy_sync.h" | |
struct ldisc_info { | |
int n; | |
char *name; | |
}; | |
static struct ldisc_info ldiscs[] = { | |
{2, "mouse"}, | |
{1, "SLIP"}, | |
{3, "Async PPP"}, | |
{5, "AX25/KISS"}, | |
{13, "HDLC"}, | |
{14, "Sync PPP"}, | |
{17, "SLCAN"}, | |
{18, "PPS"}, | |
{20, "CAIF"}, | |
{21, "GSM"} | |
}; | |
static struct tst_fzsync_pair fzp; | |
static volatile int ptmx; | |
static void *hangup(void *unused) | |
{ | |
int i; | |
while (tst_fzsync_run_b(&fzp)) { | |
tst_fzsync_start_race_b(&fzp); | |
for (i = 0; i < 10; i++) { | |
if (tcflush(ptmx, TCIFLUSH)) | |
tst_brk(TBROK | TERRNO, "tcflush(ptmx, TCIFLUSH)"); | |
} | |
tst_fzsync_end_race_b(&fzp); | |
} | |
return unused; | |
} | |
static int set_ldisc(int tty, struct ldisc_info *ldisc) | |
{ | |
TEST(ioctl(tty, TIOCSETD, &ldisc->n)); | |
if (!TST_RET) | |
return 0; | |
if (TST_ERR == EINVAL) { | |
tst_res(TCONF | TTERRNO, | |
"You don't appear to have the %s TTY line discipline", | |
ldisc->name); | |
} else { | |
tst_res(TFAIL | TTERRNO, | |
"Failed to set the %s line discipline", ldisc->name); | |
} | |
return 1; | |
} | |
static void do_test(unsigned int n) | |
{ | |
int pts; | |
char pts_path[PATH_MAX]; | |
struct ldisc_info *ldisc = &ldiscs[n + 1]; | |
tst_res(TINFO, "Creating PTY with %s line discipline", ldisc->name); | |
tst_fzsync_pair_reset(&fzp, hangup); | |
while (tst_fzsync_run_a(&fzp)) { | |
ptmx = SAFE_OPEN("/dev/ptmx", O_RDONLY); | |
if (grantpt(ptmx)) | |
tst_brk(TBROK | TERRNO, "grantpt(ptmx)"); | |
if (unlockpt(ptmx)) | |
tst_brk(TBROK | TERRNO, "unlockpt(ptmx)"); | |
if (ptsname_r(ptmx, pts_path, sizeof(pts_path))) | |
tst_brk(TBROK | TERRNO, "ptsname_r(ptmx, ...)"); | |
pts = SAFE_OPEN(pts_path, O_RDONLY); | |
if (set_ldisc(pts, &ldiscs[0])) | |
tst_brk(TCONF, "Need to set mouse discipline first"); | |
if (set_ldisc(pts, ldisc)) | |
return; | |
tst_fzsync_start_race_a(&fzp); | |
ioctl(pts, TIOCVHANGUP); | |
tst_fzsync_end_race_a(&fzp); | |
SAFE_CLOSE(pts); | |
SAFE_CLOSE(ptmx); | |
} | |
tst_res(TPASS, "Did not crash with %s TTY discipline", ldisc->name); | |
} | |
static void setup(void) | |
{ | |
fzp.min_samples = 20; | |
tst_fzsync_pair_init(&fzp); | |
} | |
static void cleanup(void) | |
{ | |
tst_fzsync_pair_cleanup(&fzp); | |
} | |
static struct tst_test test = { | |
.test = do_test, | |
.tcnt = 9, | |
.setup = setup, | |
.cleanup = cleanup, | |
.needs_root = 1, | |
.max_runtime = 30, | |
.needs_kconfigs = (const char *const[]){ | |
"CONFIG_SERIO_SERPORT", | |
NULL | |
}, | |
.tags = (const struct tst_tag[]) { | |
{"linux-git", "0ace17d568241"}, | |
{"CVE", "2020-14416"}, | |
{"linux-git", "dd42bf1197144"}, | |
{} | |
} | |
}; |