diff options
author | Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> | 2009-11-17 21:35:43 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 18:18:05 -0500 |
commit | edfacdd6f81119b9005615593f2cbd94b8c7e2d8 (patch) | |
tree | 9cdd79e4cc6f57b01848a20d5f79fcf46d6e9f16 /fs/devpts | |
parent | 68cb4f8e246bbbc649980be0628cae9265870a91 (diff) |
devpts_get_tty() should validate inode
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>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/devpts')
-rw-r--r-- | fs/devpts/inode.c | 16 |
1 files changed, 14 insertions, 2 deletions
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index d5f8c96964be..8882ecc0f1bf 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c | |||
@@ -517,11 +517,23 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) | |||
517 | 517 | ||
518 | struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number) | 518 | struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number) |
519 | { | 519 | { |
520 | struct dentry *dentry; | ||
521 | struct tty_struct *tty; | ||
522 | |||
520 | BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR)); | 523 | BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR)); |
521 | 524 | ||
525 | /* Ensure dentry has not been deleted by devpts_pty_kill() */ | ||
526 | dentry = d_find_alias(pts_inode); | ||
527 | if (!dentry) | ||
528 | return NULL; | ||
529 | |||
530 | tty = NULL; | ||
522 | if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) | 531 | if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) |
523 | return (struct tty_struct *)pts_inode->i_private; | 532 | tty = (struct tty_struct *)pts_inode->i_private; |
524 | return NULL; | 533 | |
534 | dput(dentry); | ||
535 | |||
536 | return tty; | ||
525 | } | 537 | } |
526 | 538 | ||
527 | void devpts_pty_kill(struct tty_struct *tty) | 539 | void devpts_pty_kill(struct tty_struct *tty) |