X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Ftty_io.c;h=91f530c6170ee73ba76f3b12bfd517518289fcaa;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=e4607d86a7559453977eda134b428367e56659b3;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index e4607d86a..91f530c61 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -91,6 +91,7 @@ #include #include #include +#include #include #include @@ -128,15 +129,17 @@ DECLARE_MUTEX(tty_sem); #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 *); @@ -339,21 +342,15 @@ int tty_check_change(struct tty_struct * tty) 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. */ - if (ppos != &file->f_pos) - return -ESPIPE; 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. */ - if (ppos != &file->f_pos) - return -ESPIPE; return -EIO; } @@ -638,17 +635,13 @@ void start_tty(struct tty_struct *tty) 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; struct tty_struct * tty; struct inode *inode; - /* Can't seek (pread) on ttys. */ - if (ppos != &file->f_pos) - return -ESPIPE; - tty = (struct tty_struct *)file->private_data; inode = file->f_dentry->d_inode; if (tty_paranoia_check(tty, inode, "tty_read")) @@ -672,10 +665,10 @@ static ssize_t tty_read(struct file * file, char * buf, size_t count, * 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; @@ -717,16 +710,12 @@ static inline ssize_t do_tty_write( } -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; struct inode *inode = file->f_dentry->d_inode; - /* Can't seek (pwrite) on ttys. */ - if (ppos != &file->f_pos) - return -ESPIPE; - tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_write")) return -EIO; @@ -735,10 +724,10 @@ static ssize_t tty_write(struct file * file, const char * buf, size_t count, 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; @@ -752,9 +741,6 @@ ssize_t redirected_tty_write(struct file * file, const char * buf, size_t count, if (p) { ssize_t res; - /* Can't seek (pwrite) on ttys. */ - if (ppos != &file->f_pos) - return -ESPIPE; res = vfs_write(p, buf, count, &p->f_pos); fput(p); return res; @@ -1065,6 +1051,7 @@ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; + int devpts_master, devpts; int idx; char buf[64]; @@ -1079,6 +1066,8 @@ static void release_dev(struct file * filp) idx = tty->index; pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); + devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; + devpts_master = pty_master && devpts; o_tty = tty->link; #ifdef TTY_PARANOIA_CHECK @@ -1266,6 +1255,18 @@ static void release_dev(struct file * filp) 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?? @@ -1282,24 +1283,22 @@ static void release_dev(struct file * filp) 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) { + down(&allocated_ptys_lock); + idr_remove(&allocated_ptys, idx); + up(&allocated_ptys_lock); + } +#endif + } /* @@ -1322,8 +1321,13 @@ static int tty_open(struct inode * inode, struct file * filp) int index; dev_t device = inode->i_rdev; unsigned short saved_flags = filp->f_flags; + + nonseekable_open(inode, filp); retry_open: noctty = filp->f_flags & O_NOCTTY; + index = -1; + retval = 0; + if (device == MKDEV(TTYAUX_MAJOR,0)) { if (!current->signal->tty) return -ENXIO; @@ -1344,13 +1348,8 @@ retry_open: } #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; @@ -1361,24 +1360,40 @@ retry_open: #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 { @@ -1400,10 +1415,12 @@ got_driver: #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)) @@ -1415,6 +1432,14 @@ got_driver: 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; @@ -1490,19 +1515,19 @@ static int tty_fasync(int fd, struct file * filp, int on) 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; @@ -1510,7 +1535,7 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize * arg) } static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, - struct winsize * arg) + struct winsize __user * arg) { struct winsize tmp_ws; @@ -1565,11 +1590,11 @@ static int tioccons(struct file *file) } -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) @@ -1620,7 +1645,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) 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 @@ -1628,10 +1653,10 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t */ 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); @@ -1644,7 +1669,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t (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; @@ -1654,7 +1679,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t 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 @@ -1664,14 +1689,14 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t * 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); } @@ -1690,7 +1715,7 @@ static int send_break(struct tty_struct *tty, int duration) } 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; @@ -1698,21 +1723,21 @@ tty_tiocmget(struct tty_struct *tty, struct file *file, unsigned long arg) 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; @@ -1745,6 +1770,7 @@ int tty_ioctl(struct inode * inode, struct file * file, 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; @@ -1802,15 +1828,15 @@ int tty_ioctl(struct inode * inode, struct file * file, 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; @@ -1829,15 +1855,15 @@ int tty_ioctl(struct inode * inode, struct file * file, 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); @@ -1865,12 +1891,12 @@ int tty_ioctl(struct inode * inode, struct file * file, 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);