Skip to contentSkip to conversation

tty: close registration race and add termios reset feature

PATCH In review Johan Hovold 2 participants 5 messages Mar 30, 2017 – Mar 31, 2017 TTY layer and serial drivers

lore
Share & tools
lore
#0 lore raw

cover tty: close registration race and add termios reset feature

The first patch in this series closes a race between tty device registration and tty open due to the character device being registered before the class device has been registered.

The second patch removes some obsolete references to termios_locked, and the final patch makes sure that any saved termios state is reset during device registration when reusing a minor number.

Johan

Johan Hovold (3):
tty: close race between device register and open
tty: drop obsolete termios_locked comments
tty: reset termios state on device registration

 drivers/tty/tty_io.c | 58 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 32 insertions(+), 26 deletions(-)
Signature · 1 line
2.12.2
#1 lore raw

PATCH 1/3 tty: close race between device register and open

The tty class device is currently not registered until after the character device has been registered thereby leaving a small window were a racing open could end up with a NULL tty->dev pointer due to the class-device lookup failing in alloc_tty_struct.

Close this race by registering the class device before the character device while making sure to defer the user-space uevent notification until after the character device has been registered.

Note that some tty drivers expect a valid tty->dev and would misbehave or crash otherwise. Some line disciplines also currently dereference the class device unconditionally despite the fact that not every tty is guaranteed to have one (Unix98 pty), but this is being fixed separately.

---
 drivers/tty/tty_io.c | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)
drivers/tty/tty_io.c +20 -20
index de4543ee290b..8e9651579d50 100644
@@ -3293,9 +3293,8 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
 {
 	char name[64];
 	dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
-	struct device *dev = NULL;
-	int retval = -ENODEV;
-	bool cdev = false;
+	struct device *dev;
+	int retval;
 
 	if (index >= driver->num) {
 		pr_err("%s: Attempt to register invalid tty line number (%d)\n",
@@ -3308,18 +3307,9 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
 	else
 		tty_line_name(driver, index, name);
 
-	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
-		retval = tty_cdev_add(driver, devt, index, 1);
-		if (retval)
-			goto error;
-		cdev = true;
-	}
-
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev) {
-		retval = -ENOMEM;
-		goto error;
-	}
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
 
 	dev->devt = devt;
 	dev->class = tty_class;
@@ -3329,18 +3319,28 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
 	dev->groups = attr_grp;
 	dev_set_drvdata(dev, drvdata);
 
+	dev_set_uevent_suppress(dev, 1);
+
 	retval = device_register(dev);
 	if (retval)
-		goto error;
+		goto err_put;
+
+	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
+		retval = tty_cdev_add(driver, devt, index, 1);
+		if (retval)
+			goto err_del;
+	}
+
+	dev_set_uevent_suppress(dev, 0);
+	kobject_uevent(&dev->kobj, KOBJ_ADD);
 
 	return dev;
 
-error:
+err_del:
+	device_del(dev);
+err_put:
 	put_device(dev);
-	if (cdev) {
-		cdev_del(driver->cdevs[index]);
-		driver->cdevs[index] = NULL;
-	}
+
 	return ERR_PTR(retval);
 }
 EXPORT_SYMBOL_GPL(tty_register_device_attr);
Signature · 1 line
2.12.2
#3 lore raw

PATCH 3/3 tty: reset termios state on device registration

In reply to Johan Hovold

Free any saved termios data when registering a tty device so that the termios state is reset when reusing a minor number.

This is useful for hot-pluggable buses such as USB where it does not make much sense to reuse saved termios data from an unrelated device when a new device is later plugged in.

This specifically avoids a situation where the new device does not have the carrier-detect signal wired, but the saved termios state has CLOCAL cleared, effectively preventing the port from being opened in blocking mode as noted by Jan Kundrát <[email protected]>.

Note that clearing the saved data at deregistration would not work as the device could still be open.

Also note that the termios data is not reset for drivers with TTY_DRIVER_DYNAMIC_ALLOC set (e.g. legacy pty) as their character device is registered at driver registration and could theoretically already have been opened (and pty termios state is never saved anyway).

---
 drivers/tty/tty_io.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/tty/tty_io.c +11 -0
index 5f834dcb0b15..d697053f5e42 100644
@@ -3293,6 +3293,7 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
 {
 	char name[64];
 	dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
+	struct ktermios *tp;
 	struct device *dev;
 	int retval;
 
@@ -3326,6 +3327,16 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
 		goto err_put;
 
 	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
+		/*
+		 * Free any saved termios data so that the termios state is
+		 * reset when reusing a minor number.
+		 */
+		tp = driver->termios[index];
+		if (tp) {
+			driver->termios[index] = NULL;
+			kfree(tp);
+		}
+
 		retval = tty_cdev_add(driver, devt, index, 1);
 		if (retval)
 			goto err_del;
Signature · 1 line
2.12.2
#2 lore raw

PATCH 2/3 tty: drop obsolete termios_locked comments

In reply to Johan Hovold

Drop comments about tty-driver termios_locked structures, which have been outdated since commit fe6e29fdb1a7 ("tty: simplify ktermios allocation").

---
 drivers/tty/tty_io.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
drivers/tty/tty_io.c +1 -6
index 8e9651579d50..5f834dcb0b15 100644
@@ -1520,7 +1520,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
 	 * This code guarantees that either everything succeeds and the
 	 * TTY is ready for operation, or else the table slots are vacated
 	 * and the allocated memory released.  (Except that the termios
-	 * and locked termios may be retained.)
+	 * may be retained.)
 	 */
 
 	if (!try_module_get(driver->owner))
@@ -3441,11 +3441,6 @@ static void destruct_tty_driver(struct kref *kref)
 	struct ktermios *tp;
 
 	if (driver->flags & TTY_DRIVER_INSTALLED) {
-		/*
-		 * Free the termios and termios_locked structures because
-		 * we don't want to get memory leaks when modular tty
-		 * drivers are removed from the kernel.
-		 */
 		for (i = 0; i < driver->num; i++) {
 			tp = driver->termios[i];
 			if (tp) {
Signature · 1 line
2.12.2
#4 lore raw
In reply to Johan Hovold
Quoted 7 lines · Johan Hovold

The first patch in this series closes a race between tty device registration and tty open due to the character device being registered before the class device has been registered.

The second patch removes some obsolete references to termios_locked, and the final patch makes sure that any saved termios state is reset during device registration when reusing a minor number.

Thanks for doing this, I had forgotten all about that report last year that this fixes up. Much appreciated.

greg k-h