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