Skip to content

Commit ca4463b

Browse files
ebiggersgregkh
authored andcommitted
vt: vt_ioctl: fix VT_DISALLOCATE freeing in-use virtual console
The VT_DISALLOCATE ioctl can free a virtual console while tty_release() is still running, causing a use-after-free in con_shutdown(). This occurs because VT_DISALLOCATE considers a virtual console's 'struct vc_data' to be unused as soon as the corresponding tty's refcount hits 0. But actually it may be still being closed. Fix this by making vc_data be reference-counted via the embedded 'struct tty_port'. A newly allocated virtual console has refcount 1. Opening it for the first time increments the refcount to 2. Closing it for the last time decrements the refcount (in tty_operations::cleanup() so that it happens late enough), as does VT_DISALLOCATE. Reproducer: #include <fcntl.h> #include <linux/vt.h> #include <sys/ioctl.h> #include <unistd.h> int main() { if (fork()) { for (;;) close(open("/dev/tty5", O_RDWR)); } else { int fd = open("/dev/tty10", O_RDWR); for (;;) ioctl(fd, VT_DISALLOCATE, 5); } } KASAN report: BUG: KASAN: use-after-free in con_shutdown+0x76/0x80 drivers/tty/vt/vt.c:3278 Write of size 8 at addr ffff88806a4ec108 by task syz_vt/129 CPU: 0 PID: 129 Comm: syz_vt Not tainted 5.6.0-rc2 #11 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ?-20191223_100556-anatol 04/01/2014 Call Trace: [...] con_shutdown+0x76/0x80 drivers/tty/vt/vt.c:3278 release_tty+0xa8/0x410 drivers/tty/tty_io.c:1514 tty_release_struct+0x34/0x50 drivers/tty/tty_io.c:1629 tty_release+0x984/0xed0 drivers/tty/tty_io.c:1789 [...] Allocated by task 129: [...] kzalloc include/linux/slab.h:669 [inline] vc_allocate drivers/tty/vt/vt.c:1085 [inline] vc_allocate+0x1ac/0x680 drivers/tty/vt/vt.c:1066 con_install+0x4d/0x3f0 drivers/tty/vt/vt.c:3229 tty_driver_install_tty drivers/tty/tty_io.c:1228 [inline] tty_init_dev+0x94/0x350 drivers/tty/tty_io.c:1341 tty_open_by_driver drivers/tty/tty_io.c:1987 [inline] tty_open+0x3ca/0xb30 drivers/tty/tty_io.c:2035 [...] Freed by task 130: [...] kfree+0xbf/0x1e0 mm/slab.c:3757 vt_disallocate drivers/tty/vt/vt_ioctl.c:300 [inline] vt_ioctl+0x16dc/0x1e30 drivers/tty/vt/vt_ioctl.c:818 tty_ioctl+0x9db/0x11b0 drivers/tty/tty_io.c:2660 [...] Fixes: 4001d7b ("vt: push down the tty lock so we can see what is left to tackle") Cc: <[email protected]> # v3.4+ Reported-by: [email protected] Acked-by: Jiri Slaby <[email protected]> Signed-off-by: Eric Biggers <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 3b9c55e commit ca4463b

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

drivers/tty/vt/vt.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,17 @@ static void visual_deinit(struct vc_data *vc)
10751075
module_put(vc->vc_sw->owner);
10761076
}
10771077

1078+
static void vc_port_destruct(struct tty_port *port)
1079+
{
1080+
struct vc_data *vc = container_of(port, struct vc_data, port);
1081+
1082+
kfree(vc);
1083+
}
1084+
1085+
static const struct tty_port_operations vc_port_ops = {
1086+
.destruct = vc_port_destruct,
1087+
};
1088+
10781089
int vc_allocate(unsigned int currcons) /* return 0 on success */
10791090
{
10801091
struct vt_notifier_param param;
@@ -1100,6 +1111,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
11001111

11011112
vc_cons[currcons].d = vc;
11021113
tty_port_init(&vc->port);
1114+
vc->port.ops = &vc_port_ops;
11031115
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
11041116

11051117
visual_init(vc, currcons, 1);
@@ -3250,6 +3262,7 @@ static int con_install(struct tty_driver *driver, struct tty_struct *tty)
32503262

32513263
tty->driver_data = vc;
32523264
vc->port.tty = tty;
3265+
tty_port_get(&vc->port);
32533266

32543267
if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
32553268
tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
@@ -3285,6 +3298,13 @@ static void con_shutdown(struct tty_struct *tty)
32853298
console_unlock();
32863299
}
32873300

3301+
static void con_cleanup(struct tty_struct *tty)
3302+
{
3303+
struct vc_data *vc = tty->driver_data;
3304+
3305+
tty_port_put(&vc->port);
3306+
}
3307+
32883308
static int default_color = 7; /* white */
32893309
static int default_italic_color = 2; // green (ASCII)
32903310
static int default_underline_color = 3; // cyan (ASCII)
@@ -3410,7 +3430,8 @@ static const struct tty_operations con_ops = {
34103430
.throttle = con_throttle,
34113431
.unthrottle = con_unthrottle,
34123432
.resize = vt_resize,
3413-
.shutdown = con_shutdown
3433+
.shutdown = con_shutdown,
3434+
.cleanup = con_cleanup,
34143435
};
34153436

34163437
static struct cdev vc0_cdev;

drivers/tty/vt/vt_ioctl.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,8 @@ static int vt_disallocate(unsigned int vc_num)
310310
vc = vc_deallocate(vc_num);
311311
console_unlock();
312312

313-
if (vc && vc_num >= MIN_NR_CONSOLES) {
314-
tty_port_destroy(&vc->port);
315-
kfree(vc);
316-
}
313+
if (vc && vc_num >= MIN_NR_CONSOLES)
314+
tty_port_put(&vc->port);
317315

318316
return ret;
319317
}
@@ -333,10 +331,8 @@ static void vt_disallocate_all(void)
333331
console_unlock();
334332

335333
for (i = 1; i < MAX_NR_CONSOLES; i++) {
336-
if (vc[i] && i >= MIN_NR_CONSOLES) {
337-
tty_port_destroy(&vc[i]->port);
338-
kfree(vc[i]);
339-
}
334+
if (vc[i] && i >= MIN_NR_CONSOLES)
335+
tty_port_put(&vc[i]->port);
340336
}
341337
}
342338

0 commit comments

Comments
 (0)