devpts_get_tty() should validate inode
commit5451f13711a2af1fffdfe0d1e2faf02449fdab28
authorSukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
Wed, 18 Nov 2009 02:35:43 +0000 (17 18:35 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 18 Dec 2009 22:04:15 +0000 (18 14:04 -0800)
treec840293f1f018cbc9d85f936ae24e392a1e2c62a
parentbb2fb5d6a16840cf98af1a5d405a583c200d87a8
devpts_get_tty() should validate inode

commit edfacdd6f81119b9005615593f2cbd94b8c7e2d8 upstream.

devpts_get_tty() assumes that the inode passed in is associated with a valid
pty.  But if the only reference to the pty is via a bind-mount, the inode
passed to devpts_get_tty() while valid, would refer to a pty that no longer
exists.

With a lot of debug effort, Grzegorz Nosek developed a small program (see
below) to reproduce a crash on recent kernels. This crash is a regression
introduced by the commit:

commit 527b3e4773628b30d03323a2cb5fb0d84441990f
Author: Sukadev Bhattiprolu <sukadev@us.ibm.com>
Date:   Mon Oct 13 10:43:08 2008 +0100

To fix, ensure that the dentry associated with the inode has not yet been
deleted/unhashed by devpts_pty_kill().

See also:
https://lists.linux-foundation.org/pipermail/containers/2009-July/019273.html

tty-bug.c:

#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/signal.h>
#include <unistd.h>
#include <stdio.h>

#include <linux/fs.h>

void dummy(int sig)
{
}

static int child(void *unused)
{
int fd;

signal(SIGINT, dummy); signal(SIGHUP, dummy);
pause(); /* cheesy synchronisation to wait for /dev/pts/0 to appear */

mount("/dev/pts/0", "/dev/console", NULL, MS_BIND, NULL);
sleep(2);

fd = open("/dev/console", O_RDWR);
dup(0); dup(0);
write(1, "Hello world!\n", sizeof("Hello world!\n")-1);
return 0;
}

int main(void)
{
pid_t pid;
char *stack;

stack = malloc(16384);
pid = clone(child, stack+16384, CLONE_NEWNS|SIGCHLD, NULL);

open("/dev/ptmx", O_RDWR|O_NOCTTY|O_NONBLOCK);

unlockpt(fd); grantpt(fd);

sleep(2);
kill(pid, SIGHUP);
sleep(1);
return 0; /* exit before child opens /dev/console */
}

Reported-by: Grzegorz Nosek <root@localdomain.pl>
Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
Tested-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
fs/devpts/inode.c