tty: Fix unsafe ldisc reference via ioctl(TIOCGETD)
[pandora-kernel.git] / drivers / tty / tty_io.c
index 05085be..1f9ee25 100644 (file)
@@ -940,6 +940,14 @@ void start_tty(struct tty_struct *tty)
 
 EXPORT_SYMBOL(start_tty);
 
+/* We limit tty time update visibility to every 8 seconds or so. */
+static void tty_update_time(struct timespec *time)
+{
+       unsigned long sec = get_seconds();
+       if (abs(sec - time->tv_sec) & ~7)
+               time->tv_sec = sec;
+}
+
 /**
  *     tty_read        -       read method for tty device files
  *     @file: pointer to tty file
@@ -976,8 +984,10 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
        else
                i = -EIO;
        tty_ldisc_deref(ld);
+
        if (i > 0)
-               inode->i_atime = current_fs_time(inode->i_sb);
+               tty_update_time(&inode->i_atime);
+
        return i;
 }
 
@@ -1079,8 +1089,8 @@ static inline ssize_t do_tty_write(
                cond_resched();
        }
        if (written) {
-               struct inode *inode = file->f_path.dentry->d_inode;
-               inode->i_mtime = current_fs_time(inode->i_sb);
+               struct inode *inode = file->f_path.dentry->d_inode;
+               tty_update_time(&inode->i_mtime);
                ret = written;
        }
 out:
@@ -1212,9 +1222,9 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p)
  *
  *     Locking: None
  */
-static void tty_line_name(struct tty_driver *driver, int index, char *p)
+static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
 {
-       sprintf(p, "%s%d", driver->name, index + driver->name_base);
+       return sprintf(p, "%s%d", driver->name, index + driver->name_base);
 }
 
 /**
@@ -1584,6 +1594,7 @@ int tty_release(struct inode *inode, struct file *filp)
        int     devpts;
        int     idx;
        char    buf[64];
+       long    timeout = 0;
 
        if (tty_paranoia_check(tty, inode, "tty_release_dev"))
                return 0;
@@ -1711,7 +1722,11 @@ int tty_release(struct inode *inode, struct file *filp)
                                    "active!\n", tty_name(tty, buf));
                tty_unlock();
                mutex_unlock(&tty_mutex);
-               schedule();
+               schedule_timeout_killable(timeout);
+               if (timeout < 120 * HZ)
+                       timeout = 2 * timeout + 1;
+               else
+                       timeout = MAX_SCHEDULE_TIMEOUT;
        }
 
        /*
@@ -1970,8 +1985,24 @@ got_driver:
        if (!noctty &&
            current->signal->leader &&
            !current->signal->tty &&
-           tty->session == NULL)
-               __proc_set_tty(current, tty);
+           tty->session == NULL) {
+               /*
+                * Don't let a process that only has write access to the tty
+                * obtain the privileges associated with having a tty as
+                * controlling terminal (being able to reopen it with full
+                * access through /dev/tty, being able to perform pushback).
+                * Many distributions set the group of all ttys to "tty" and
+                * grant write-only access to all terminals for setgid tty
+                * binaries, which should not imply full privileges on all ttys.
+                *
+                * This could theoretically break old code that performs open()
+                * on a write-only file descriptor. In that case, it might be
+                * necessary to also permit this if
+                * inode_permission(inode, MAY_READ) == 0.
+                */
+               if (filp->f_mode & FMODE_READ)
+                       __proc_set_tty(current, tty);
+       }
        spin_unlock_irq(&current->sighand->siglock);
        tty_unlock();
        mutex_unlock(&tty_mutex);
@@ -2251,7 +2282,7 @@ static int fionbio(struct file *file, int __user *p)
  *             Takes ->siglock() when updating signal->tty
  */
 
-static int tiocsctty(struct tty_struct *tty, int arg)
+static int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
 {
        int ret = 0;
        if (current->signal->leader && (task_session(current) == tty->session))
@@ -2284,6 +2315,13 @@ static int tiocsctty(struct tty_struct *tty, int arg)
                        goto unlock;
                }
        }
+
+       /* See the comment in tty_open(). */
+       if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) {
+               ret = -EPERM;
+               goto unlock;
+       }
+
        proc_set_tty(current, tty);
 unlock:
        mutex_unlock(&tty_mutex);
@@ -2436,6 +2474,28 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
        return ret;
 }
 
+/**
+ *     tiocgetd        -       get line discipline
+ *     @tty: tty device
+ *     @p: pointer to user data
+ *
+ *     Retrieves the line discipline id directly from the ldisc.
+ *
+ *     Locking: waits for ldisc reference (in case the line discipline
+ *             is changing or the tty is being hungup)
+ */
+
+static int tiocgetd(struct tty_struct *tty, int __user *p)
+{
+       struct tty_ldisc *ld;
+       int ret;
+
+       ld = tty_ldisc_ref_wait(tty);
+       ret = put_user(ld->ops->num, p);
+       tty_ldisc_deref(ld);
+       return ret;
+}
+
 /**
  *     send_break      -       performed time break
  *     @tty: device to break on
@@ -2638,7 +2698,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                no_tty();
                return 0;
        case TIOCSCTTY:
-               return tiocsctty(tty, arg);
+               return tiocsctty(tty, file, arg);
        case TIOCGPGRP:
                return tiocgpgrp(tty, real_tty, p);
        case TIOCSPGRP:
@@ -2646,7 +2706,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCGSID:
                return tiocgsid(tty, real_tty, p);
        case TIOCGETD:
-               return put_user(tty->ldisc->ops->num, (int __user *)p);
+               return tiocgetd(tty, p);
        case TIOCSETD:
                return tiocsetd(tty, p);
        case TIOCVHANGUP:
@@ -3311,9 +3371,19 @@ static ssize_t show_cons_active(struct device *dev,
                if (i >= ARRAY_SIZE(cs))
                        break;
        }
-       while (i--)
-               count += sprintf(buf + count, "%s%d%c",
-                                cs[i]->name, cs[i]->index, i ? ' ':'\n');
+       while (i--) {
+               int index = cs[i]->index;
+               struct tty_driver *drv = cs[i]->device(cs[i], &index);
+
+               /* don't resolve tty0 as some programs depend on it */
+               if (drv && (cs[i]->index > 0 || drv->major != TTY_MAJOR))
+                       count += tty_line_name(drv, index, buf + count);
+               else
+                       count += sprintf(buf + count, "%s%d",
+                                        cs[i]->name, cs[i]->index);
+
+               count += sprintf(buf + count, "%c", i ? ' ':'\n');
+       }
        console_unlock();
 
        return count;