]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/tty/tty_io.c
tty: Fix ldisc leak in failed tty_init_dev()
[karo-tx-linux.git] / drivers / tty / tty_io.c
index bcc8e1e8bb720996cf73438c67a397362173edca..c9f2365167df459e4450471fb5874dfd24a6f06e 100644 (file)
@@ -172,6 +172,7 @@ void free_tty_struct(struct tty_struct *tty)
 {
        if (!tty)
                return;
+       tty_ldisc_deinit(tty);
        put_device(tty->dev);
        kfree(tty->write_buf);
        tty->magic = 0xDEADDEAD;
@@ -256,19 +257,24 @@ const char *tty_name(const struct tty_struct *tty)
 
 EXPORT_SYMBOL(tty_name);
 
-int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
+const char *tty_driver_name(const struct tty_struct *tty)
+{
+       if (!tty || !tty->driver)
+               return "";
+       return tty->driver->name;
+}
+
+static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
                              const char *routine)
 {
 #ifdef TTY_PARANOIA_CHECK
        if (!tty) {
-               printk(KERN_WARNING
-                       "null TTY for (%d:%d) in %s\n",
+               pr_warn("(%d:%d): %s: NULL tty\n",
                        imajor(inode), iminor(inode), routine);
                return 1;
        }
        if (tty->magic != TTY_MAGIC) {
-               printk(KERN_WARNING
-                       "bad magic number for tty struct (%d:%d) in %s\n",
+               pr_warn("(%d:%d): %s: bad magic number\n",
                        imajor(inode), iminor(inode), routine);
                return 1;
        }
@@ -293,9 +299,8 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
            tty->link && tty->link->count)
                count++;
        if (tty->count != count) {
-               printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
-                                   "!= #fd's(%d) in %s\n",
-                      tty->name, tty->count, count, routine);
+               tty_warn(tty, "%s: tty->count(%d) != #fd's(%d)\n",
+                        routine, tty->count, count);
                return count;
        }
 #endif
@@ -420,10 +425,8 @@ int __tty_check_change(struct tty_struct *tty, int sig)
        }
        rcu_read_unlock();
 
-       if (!tty_pgrp) {
-               pr_warn("%s: tty_check_change: sig=%d, tty->pgrp == NULL!\n",
-                       tty_name(tty), sig);
-       }
+       if (!tty_pgrp)
+               tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig);
 
        return ret;
 }
@@ -781,7 +784,7 @@ static void do_tty_hangup(struct work_struct *work)
 
 void tty_hangup(struct tty_struct *tty)
 {
-       tty_debug_hangup(tty, "\n");
+       tty_debug_hangup(tty, "hangup\n");
        schedule_work(&tty->hangup_work);
 }
 
@@ -798,7 +801,7 @@ EXPORT_SYMBOL(tty_hangup);
 
 void tty_vhangup(struct tty_struct *tty)
 {
-       tty_debug_hangup(tty, "\n");
+       tty_debug_hangup(tty, "vhangup\n");
        __tty_hangup(tty, 0);
 }
 
@@ -835,7 +838,7 @@ void tty_vhangup_self(void)
 
 static void tty_vhangup_session(struct tty_struct *tty)
 {
-       tty_debug_hangup(tty, "\n");
+       tty_debug_hangup(tty, "session hangup\n");
        __tty_hangup(tty, 1);
 }
 
@@ -1239,8 +1242,7 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
                        return -EIO;
        /* Short term debug to catch buggy drivers */
        if (tty->ops->write_room == NULL)
-               printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
-                       tty->driver->name);
+               tty_err(tty, "missing write_room method\n");
        ld = tty_ldisc_ref_wait(tty);
        if (!ld->ops->write)
                ret = -EIO;
@@ -1462,13 +1464,13 @@ static int tty_reopen(struct tty_struct *tty)
 {
        struct tty_driver *driver = tty->driver;
 
-       if (!tty->count)
-               return -EIO;
-
        if (driver->type == TTY_DRIVER_TYPE_PTY &&
            driver->subtype == PTY_TYPE_MASTER)
                return -EIO;
 
+       if (!tty->count)
+               return -EAGAIN;
+
        if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
                return -EBUSY;
 
@@ -1528,7 +1530,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
        tty_lock(tty);
        retval = tty_driver_install_tty(driver, tty);
        if (retval < 0)
-               goto err_deinit_tty;
+               goto err_free_tty;
 
        if (!tty->port)
                tty->port = driver->ports[idx];
@@ -1550,9 +1552,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
        /* Return the tty locked so that it cannot vanish under the caller */
        return tty;
 
-err_deinit_tty:
+err_free_tty:
        tty_unlock(tty);
-       deinitialize_tty_struct(tty);
        free_tty_struct(tty);
 err_module_put:
        module_put(driver->owner);
@@ -1561,8 +1562,8 @@ err_module_put:
        /* call the tty release_tty routine to clean out this slot */
 err_release_tty:
        tty_unlock(tty);
-       printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, "
-                                "clearing slot %d\n", idx);
+       tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
+                            retval, idx);
        release_tty(tty, idx);
        return ERR_PTR(retval);
 }
@@ -1580,10 +1581,8 @@ void tty_free_termios(struct tty_struct *tty)
        tp = tty->driver->termios[idx];
        if (tp == NULL) {
                tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
-               if (tp == NULL) {
-                       pr_warn("tty: no memory to save termios state.\n");
+               if (tp == NULL)
                        return;
-               }
                tty->driver->termios[idx] = tp;
        }
        *tp = tty->termios;
@@ -1788,7 +1787,7 @@ int tty_release(struct inode *inode, struct file *filp)
                return 0;
        }
 
-       tty_debug_hangup(tty, "(tty count=%d)...\n", tty->count);
+       tty_debug_hangup(tty, "releasing (count=%d)\n", tty->count);
 
        if (tty->ops->close)
                tty->ops->close(tty, filp);
@@ -1837,8 +1836,7 @@ int tty_release(struct inode *inode, struct file *filp)
 
                if (once) {
                        once = 0;
-                       printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
-                              __func__, tty_name(tty));
+                       tty_warn(tty, "read/write wait queue active!\n");
                }
                schedule_timeout_killable(timeout);
                if (timeout < 120 * HZ)
@@ -1849,14 +1847,12 @@ int tty_release(struct inode *inode, struct file *filp)
 
        if (o_tty) {
                if (--o_tty->count < 0) {
-                       printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n",
-                               __func__, o_tty->count, tty_name(o_tty));
+                       tty_warn(tty, "bad slave count (%d)\n", o_tty->count);
                        o_tty->count = 0;
                }
        }
        if (--tty->count < 0) {
-               printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n",
-                               __func__, tty->count, tty_name(tty));
+               tty_warn(tty, "bad tty->count (%d)\n", tty->count);
                tty->count = 0;
        }
 
@@ -1907,7 +1903,7 @@ int tty_release(struct inode *inode, struct file *filp)
        /* Wait for pending work before tty destruction commmences */
        tty_flush_works(tty);
 
-       tty_debug_hangup(tty, "freeing structure...\n");
+       tty_debug_hangup(tty, "freeing structure\n");
        /*
         * The release_tty function takes care of the details of clearing
         * the slots and preserving the termios structure. The tty_unlock_pair
@@ -2069,7 +2065,12 @@ retry_open:
 
                if (tty) {
                        mutex_unlock(&tty_mutex);
-                       tty_lock(tty);
+                       retval = tty_lock_interruptible(tty);
+                       if (retval) {
+                               if (retval == -EINTR)
+                                       retval = -ERESTARTSYS;
+                               goto err_unref;
+                       }
                        /* safe to drop the kref from tty_driver_lookup_tty() */
                        tty_kref_put(tty);
                        retval = tty_reopen(tty);
@@ -2087,7 +2088,11 @@ retry_open:
 
        if (IS_ERR(tty)) {
                retval = PTR_ERR(tty);
-               goto err_file;
+               if (retval != -EAGAIN || signal_pending(current))
+                       goto err_file;
+               tty_free_file(filp);
+               schedule();
+               goto retry_open;
        }
 
        tty_add_file(tty, filp);
@@ -2097,7 +2102,7 @@ retry_open:
            tty->driver->subtype == PTY_TYPE_MASTER)
                noctty = 1;
 
-       tty_debug_hangup(tty, "(tty count=%d)\n", tty->count);
+       tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
 
        if (tty->ops->open)
                retval = tty->ops->open(tty, filp);
@@ -2106,7 +2111,7 @@ retry_open:
        filp->f_flags = saved_flags;
 
        if (retval) {
-               tty_debug_hangup(tty, "error %d, releasing...\n", retval);
+               tty_debug_hangup(tty, "open error %d, releasing\n", retval);
 
                tty_unlock(tty); /* need to call tty_release without BTM */
                tty_release(inode, filp);
@@ -2156,6 +2161,7 @@ retry_open:
        return 0;
 err_unlock:
        mutex_unlock(&tty_mutex);
+err_unref:
        /* after locks to avoid deadlock */
        if (!IS_ERR_OR_NULL(driver))
                tty_driver_kref_put(driver);
@@ -2652,6 +2658,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
@@ -2870,7 +2898,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                no_tty();
                return 0;
        case TIOCSCTTY:
-               return tiocsctty(tty, file, arg);
+               return tiocsctty(real_tty, file, arg);
        case TIOCGPGRP:
                return tiocgpgrp(tty, real_tty, p);
        case TIOCSPGRP:
@@ -2878,7 +2906,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:
@@ -3028,28 +3056,24 @@ void __do_SAK(struct tty_struct *tty)
        read_lock(&tasklist_lock);
        /* Kill the entire session */
        do_each_pid_task(session, PIDTYPE_SID, p) {
-               printk(KERN_NOTICE "SAK: killed process %d"
-                       " (%s): task_session(p)==tty->session\n",
-                       task_pid_nr(p), p->comm);
+               tty_notice(tty, "SAK: killed process %d (%s): by session\n",
+                          task_pid_nr(p), p->comm);
                send_sig(SIGKILL, p, 1);
        } while_each_pid_task(session, PIDTYPE_SID, p);
-       /* Now kill any processes that happen to have the
-        * tty open.
-        */
+
+       /* Now kill any processes that happen to have the tty open */
        do_each_thread(g, p) {
                if (p->signal->tty == tty) {
-                       printk(KERN_NOTICE "SAK: killed process %d"
-                           " (%s): task_session(p)==tty->session\n",
-                           task_pid_nr(p), p->comm);
+                       tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n",
+                                  task_pid_nr(p), p->comm);
                        send_sig(SIGKILL, p, 1);
                        continue;
                }
                task_lock(p);
                i = iterate_fd(p->files, 0, this_tty, tty);
                if (i != 0) {
-                       printk(KERN_NOTICE "SAK: killed process %d"
-                           " (%s): fd#%d opened to the tty\n",
-                                   task_pid_nr(p), p->comm, i - 1);
+                       tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n",
+                                  task_pid_nr(p), p->comm, i - 1);
                        force_sig(SIGKILL, p);
                }
                task_unlock(p);
@@ -3138,20 +3162,6 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
        return tty;
 }
 
-/**
- *     deinitialize_tty_struct
- *     @tty: tty to deinitialize
- *
- *     This subroutine deinitializes a tty structure that has been newly
- *     allocated but tty_release cannot be called on that yet.
- *
- *     Locking: none - tty in question must not be exposed at this point
- */
-void deinitialize_tty_struct(struct tty_struct *tty)
-{
-       tty_ldisc_deinit(tty);
-}
-
 /**
  *     tty_put_char    -       write one character to a tty
  *     @tty: tty
@@ -3219,7 +3229,7 @@ EXPORT_SYMBOL(tty_register_device);
 
 static void tty_device_create_release(struct device *dev)
 {
-       pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
+       dev_dbg(dev, "releasing...\n");
        kfree(dev);
 }
 
@@ -3255,8 +3265,8 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
        bool cdev = false;
 
        if (index >= driver->num) {
-               printk(KERN_ERR "Attempt to register invalid tty line number "
-                      " (%d).\n", index);
+               pr_err("%s: Attempt to register invalid tty line number (%d)\n",
+                      driver->name, index);
                return ERR_PTR(-EINVAL);
        }