[BUG] tiocsti() NULL dereference if ld->ops->receive_buf==NULL

From: Jann Horn
Date: Fri Jan 18 2019 - 14:09:36 EST


Hi!

When a line discipline doesn't have a ->receive_buf handler, tiocsti()
attempts to call a NULL pointer. Both tty_n_tracesink and
spk_ttyio_ldisc_ops don't have such a handler.

To reproduce, build a kernel with CONFIG_SPEAKUP=y and
CONFIG_SPEAKUP_SYNTH_SOFT=y, set speakup.synth=soft in the kernel
command line, and run the following code as root:

=================
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <err.h>

void do_ioctl(int fd, unsigned long cmd, void *arg) {
int res = ioctl(fd, cmd, arg);
if (res < 0) {
perror("ioctl");
} else {
printf("result %d\n", res);
}
}

int main(void) {
int fd = open("/dev/tty1", O_RDWR);
if (fd == -1) err(1, "open");

int disc = 26/*N_SPEAKUP*/;
do_ioctl(fd, TIOCSETD, &disc);

char c = 'A';
do_ioctl(fd, TIOCSTI, &c);

return 0;
}
=================

You should get this splat:
=================
[ 124.609859] BUG: unable to handle kernel NULL pointer dereference
at 0000000000000000
[ 124.613510] #PF error: [INSTR]
[ 124.614545] PGD 0 P4D 0
[ 124.615249] Oops: 0010 [#1] PREEMPT SMP DEBUG_PAGEALLOC KASAN
[...]
[ 124.620994] RIP: 0010: (null)
[ 124.622016] Code: Bad RIP value.
[ 124.622884] RSP: 0018:ffff8881d6c37ae0 EFLAGS: 00010246
[...]
[ 124.643743] Call Trace:
[ 124.644428] ? tty_ioctl+0x5ec/0xbd0
[ 124.645404] ? tty_vhangup+0x20/0x20
[ 124.646380] ? preempt_count_add+0x8d/0xe0
[ 124.647476] ? _raw_spin_lock_irqsave+0x9a/0xf0
[ 124.648702] ? _raw_read_lock_irqsave+0x60/0x60
[ 124.649966] ? __wake_up_common+0x4b/0x1d0
[ 124.651075] ? preempt_count_sub+0x14/0xc0
[ 124.652180] ? _raw_spin_unlock_irqrestore+0x20/0x40
[ 124.653508] ? __wake_up_common_lock+0xd7/0x130
[ 124.654740] ? __wake_up_common+0x1d0/0x1d0
[ 124.655870] ? mutex_trylock+0x5f/0x90
[ 124.656897] ? ldsem_up_read+0x13/0x40
[ 124.657940] ? tty_write+0x2c7/0x450
[ 124.658902] ? n_tty_open+0xd0/0xd0
[ 124.659868] ? __vfs_write+0xc9/0x3b0
[ 124.660876] ? __anon_vma_prepare+0x159/0x240
[ 124.662057] ? preempt_count_add+0x8d/0xe0
[ 124.663170] ? do_vfs_ioctl+0x134/0x8f0
[ 124.664220] ? fsnotify+0x5ba/0x5e0
[ 124.665169] ? __lru_cache_add+0xc4/0x100
[ 124.666255] ? ioctl_preallocate+0x140/0x140
[ 124.667383] ? __fsnotify_inode_delete+0x20/0x20
[ 124.668640] ? vfs_write+0x120/0x230
[ 124.669613] ? ksys_ioctl+0x70/0x80
[ 124.670570] ? __x64_sys_ioctl+0x3d/0x50
[ 124.671634] ? do_syscall_64+0x73/0x160
[ 124.672690] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 124.674099] Modules linked in: btrfs xor zstd_compress raid6_pq
[ 124.676617] CR2: 0000000000000000
[ 124.677523] ---[ end trace c5f555a92fe90edc ]---
[ 124.678753] RIP: 0010: (null)
[ 124.679762] Code: Bad RIP value.
[...]
=================

I'm not familiar with the details of the TTY subsystem, so I'm not
sure how this should be fixed.