#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/device.h>
+#include <linux/idr.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
extern int pty_limit; /* Config limit on Unix98 ptys */
+static DEFINE_IDR(allocated_ptys);
+static DECLARE_MUTEX(allocated_ptys_lock);
#endif
extern void disable_early_printk(void);
static void initialize_tty_struct(struct tty_struct *tty);
-static ssize_t tty_read(struct file *, char *, size_t, loff_t *);
-static ssize_t tty_write(struct file *, const char *, size_t, loff_t *);
-ssize_t redirected_tty_write(struct file *, const char *, size_t, loff_t *);
+static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
+ssize_t redirected_tty_write(struct file *, const char __user *, size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *);
EXPORT_SYMBOL(tty_check_change);
-static ssize_t hung_up_tty_read(struct file * file, char * buf,
+static ssize_t hung_up_tty_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
/* Can't seek (pread) on ttys. */
return 0;
}
-static ssize_t hung_up_tty_write(struct file * file, const char * buf,
+static ssize_t hung_up_tty_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
/* Can't seek (pwrite) on ttys. */
EXPORT_SYMBOL(start_tty);
-static ssize_t tty_read(struct file * file, char * buf, size_t count,
+static ssize_t tty_read(struct file * file, char __user * buf, size_t count,
loff_t *ppos)
{
int i;
* denial-of-service type attacks
*/
static inline ssize_t do_tty_write(
- ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
+ ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char __user *, size_t),
struct tty_struct *tty,
struct file *file,
- const unsigned char *buf,
+ const unsigned char __user *buf,
size_t count)
{
ssize_t ret = 0, written = 0;
}
-static ssize_t tty_write(struct file * file, const char * buf, size_t count,
+static ssize_t tty_write(struct file * file, const char __user * buf, size_t count,
loff_t *ppos)
{
struct tty_struct * tty;
if (!tty->ldisc.write)
return -EIO;
return do_tty_write(tty->ldisc.write, tty, file,
- (const unsigned char *)buf, count);
+ (const unsigned char __user *)buf, count);
}
-ssize_t redirected_tty_write(struct file * file, const char * buf, size_t count,
+ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count,
loff_t *ppos)
{
struct file *p = NULL;
{
struct tty_struct *tty, *o_tty;
int pty_master, tty_closing, o_tty_closing, do_sleep;
+ int devpts_master;
int idx;
char buf[64];
idx = tty->index;
pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER);
+ devpts_master = pty_master && (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM);
o_tty = tty->link;
#ifdef TTY_PARANOIA_CHECK
printk(KERN_DEBUG "freeing tty structure...");
#endif
+ /*
+ * Prevent flush_to_ldisc() from rescheduling the work for later. Then
+ * kill any delayed work.
+ */
+ clear_bit(TTY_DONT_FLIP, &tty->flags);
+ cancel_delayed_work(&tty->flip.work);
+
+ /*
+ * Wait for ->hangup_work and ->flip.work handlers to terminate
+ */
+ flush_scheduled_work();
+
/*
* Shutdown the current line discipline, and reset it to N_TTY.
* N.B. why reset ldisc when we're releasing the memory??
module_put(o_tty->ldisc.owner);
o_tty->ldisc = ldiscs[N_TTY];
}
-
- /*
- * Prevent flush_to_ldisc() from rescheduling the work for later. Then
- * kill any delayed work.
- */
- clear_bit(TTY_DONT_FLIP, &tty->flags);
- cancel_delayed_work(&tty->flip.work);
/*
- * Wait for ->hangup_work and ->flip.work handlers to terminate
- */
- flush_scheduled_work();
-
- /*
* The release_mem function takes care of the details of clearing
* the slots and preserving the termios structure.
*/
release_mem(tty, idx);
+
+#ifdef CONFIG_UNIX98_PTYS
+ /* Make this pty number available for reallocation */
+ if (devpts_master) {
+ down(&allocated_ptys_lock);
+ idr_remove(&allocated_ptys, idx);
+ up(&allocated_ptys_lock);
+ }
+#endif
+
}
/*
int index;
dev_t device = inode->i_rdev;
unsigned short saved_flags = filp->f_flags;
+
retry_open:
noctty = filp->f_flags & O_NOCTTY;
+ index = -1;
+ retval = 0;
+
if (device == MKDEV(TTYAUX_MAJOR,0)) {
if (!current->signal->tty)
return -ENXIO;
}
#endif
if (device == MKDEV(TTYAUX_MAJOR,1)) {
- struct console *c = console_drivers;
- for (c = console_drivers; c; c = c->next) {
- if (!c->device)
- continue;
- driver = c->device(c, &index);
- if (!driver)
- continue;
+ driver = console_device(&index);
+ if (driver) {
/* Don't let /dev/console block */
filp->f_flags |= O_NONBLOCK;
noctty = 1;
#ifdef CONFIG_UNIX98_PTYS
if (device == MKDEV(TTYAUX_MAJOR,2)) {
+ int idr_ret;
+
/* find a device that is not in use. */
- static int next_ptmx_dev = 0;
- retval = -1;
+ down(&allocated_ptys_lock);
+ if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
+ up(&allocated_ptys_lock);
+ return -ENOMEM;
+ }
+ idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
+ if (idr_ret < 0) {
+ up(&allocated_ptys_lock);
+ if (idr_ret == -EAGAIN)
+ return -ENOMEM;
+ return -EIO;
+ }
+ if (index >= pty_limit) {
+ idr_remove(&allocated_ptys, index);
+ up(&allocated_ptys_lock);
+ return -EIO;
+ }
+ up(&allocated_ptys_lock);
+
driver = ptm_driver;
- while (driver->refcount < pty_limit) {
- index = next_ptmx_dev;
- next_ptmx_dev = (next_ptmx_dev+1) % driver->num;
- if (!init_dev(driver, index, &tty))
- goto ptmx_found; /* ok! */
+ retval = init_dev(driver, index, &tty);
+ if (retval) {
+ down(&allocated_ptys_lock);
+ idr_remove(&allocated_ptys, index);
+ up(&allocated_ptys_lock);
+ return retval;
}
- return -EIO; /* no free ptys */
- ptmx_found:
+
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
- if (devpts_pty_new(tty->link)) {
- /* BADNESS - need to destroy both ptm and pts! */
- return -ENOMEM;
- }
- noctty = 1;
+ if (devpts_pty_new(tty->link))
+ retval = -ENOMEM;
} else
#endif
{
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "opening %s...", tty->name);
#endif
- if (tty->driver->open)
- retval = tty->driver->open(tty, filp);
- else
- retval = -ENODEV;
+ if (!retval) {
+ if (tty->driver->open)
+ retval = tty->driver->open(tty, filp);
+ else
+ retval = -ENODEV;
+ }
filp->f_flags = saved_flags;
if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
tty->name);
#endif
+#ifdef CONFIG_UNIX98_PTYS
+ if (index != -1) {
+ down(&allocated_ptys_lock);
+ idr_remove(&allocated_ptys, index);
+ up(&allocated_ptys_lock);
+ }
+#endif
+
release_dev(filp);
if (retval != -ERESTARTSYS)
return retval;
return 0;
}
-static int tiocsti(struct tty_struct *tty, char * arg)
+static int tiocsti(struct tty_struct *tty, char __user *p)
{
char ch, mbz = 0;
if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
return -EPERM;
- if (get_user(ch, arg))
+ if (get_user(ch, p))
return -EFAULT;
tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
return 0;
}
-static int tiocgwinsz(struct tty_struct *tty, struct winsize * arg)
+static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
{
if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
return -EFAULT;
}
static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
- struct winsize * arg)
+ struct winsize __user * arg)
{
struct winsize tmp_ws;
}
-static int fionbio(struct file *file, int *arg)
+static int fionbio(struct file *file, int __user *p)
{
int nonblock;
- if (get_user(nonblock, arg))
+ if (get_user(nonblock, p))
return -EFAULT;
if (nonblock)
return 0;
}
-static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
+static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
{
/*
* (tty == real_tty) is a cheap way of
*/
if (tty == real_tty && current->signal->tty != real_tty)
return -ENOTTY;
- return put_user(real_tty->pgrp, arg);
+ return put_user(real_tty->pgrp, p);
}
-static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
+static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
{
pid_t pgrp;
int retval = tty_check_change(real_tty);
(current->signal->tty != real_tty) ||
(real_tty->session != current->signal->session))
return -ENOTTY;
- if (get_user(pgrp, (pid_t *) arg))
+ if (get_user(pgrp, p))
return -EFAULT;
if (pgrp < 0)
return -EINVAL;
return 0;
}
-static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
+static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
{
/*
* (tty == real_tty) is a cheap way of
return -ENOTTY;
if (real_tty->session <= 0)
return -ENOTTY;
- return put_user(real_tty->session, arg);
+ return put_user(real_tty->session, p);
}
-static int tiocsetd(struct tty_struct *tty, int *arg)
+static int tiocsetd(struct tty_struct *tty, int __user *p)
{
int ldisc;
- if (get_user(ldisc, arg))
+ if (get_user(ldisc, p))
return -EFAULT;
return tty_set_ldisc(tty, ldisc);
}
}
static int
-tty_tiocmget(struct tty_struct *tty, struct file *file, unsigned long arg)
+tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
{
int retval = -EINVAL;
retval = tty->driver->tiocmget(tty, file);
if (retval >= 0)
- retval = put_user(retval, (int *)arg);
+ retval = put_user(retval, p);
}
return retval;
}
static int
tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
- unsigned long arg)
+ unsigned __user *p)
{
int retval = -EINVAL;
if (tty->driver->tiocmset) {
unsigned int set, clear, val;
- retval = get_user(val, (unsigned int *)arg);
+ retval = get_user(val, p);
if (retval)
return retval;
unsigned int cmd, unsigned long arg)
{
struct tty_struct *tty, *real_tty;
+ void __user *p = (void __user *)arg;
int retval;
tty = (struct tty_struct *)file->private_data;
switch (cmd) {
case TIOCSTI:
- return tiocsti(tty, (char *)arg);
+ return tiocsti(tty, p);
case TIOCGWINSZ:
- return tiocgwinsz(tty, (struct winsize *) arg);
+ return tiocgwinsz(tty, p);
case TIOCSWINSZ:
- return tiocswinsz(tty, real_tty, (struct winsize *) arg);
+ return tiocswinsz(tty, real_tty, p);
case TIOCCONS:
return real_tty!=tty ? -EINVAL : tioccons(file);
case FIONBIO:
- return fionbio(file, (int *) arg);
+ return fionbio(file, p);
case TIOCEXCL:
set_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
case TIOCSCTTY:
return tiocsctty(tty, arg);
case TIOCGPGRP:
- return tiocgpgrp(tty, real_tty, (pid_t *) arg);
+ return tiocgpgrp(tty, real_tty, p);
case TIOCSPGRP:
- return tiocspgrp(tty, real_tty, (pid_t *) arg);
+ return tiocspgrp(tty, real_tty, p);
case TIOCGSID:
- return tiocgsid(tty, real_tty, (pid_t *) arg);
+ return tiocgsid(tty, real_tty, p);
case TIOCGETD:
- return put_user(tty->ldisc.num, (int *) arg);
+ return put_user(tty->ldisc.num, (int __user *)p);
case TIOCSETD:
- return tiocsetd(tty, (int *) arg);
+ return tiocsetd(tty, p);
#ifdef CONFIG_VT
case TIOCLINUX:
return tioclinux(tty, arg);
return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
case TIOCMGET:
- return tty_tiocmget(tty, file, arg);
+ return tty_tiocmget(tty, file, p);
case TIOCMSET:
case TIOCMBIC:
case TIOCMBIS:
- return tty_tiocmset(tty, file, cmd, arg);
+ return tty_tiocmset(tty, file, cmd, p);
}
if (tty->driver->ioctl) {
int retval = (tty->driver->ioctl)(tty, file, cmd, arg);