-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathpty03.c
More file actions
165 lines (139 loc) · 3.71 KB
/
Copy pathpty03.c
File metadata and controls
165 lines (139 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2020 SUSE
*/
/*\
* 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 = ARRAY_SIZE(ldiscs) - 1,
.setup = setup,
.cleanup = cleanup,
.needs_root = 1,
.runtime = 30,
.min_runtime = 16,
.needs_kconfigs = (const char *const[]){
"CONFIG_SERIO_SERPORT",
NULL
},
.tags = (const struct tst_tag[]) {
{"linux-git", "0ace17d56824165c7f4c68785d6b58971db954dd"},
{"CVE", "2020-14416"},
{"linux-git", "dd42bf1197144ede075a9d4793123f7689e164bc"},
{}
}
};