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