linux-serial.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] tty: remove unused argument from tty_open_by_driver()
@ 2019-11-20 15:17 Sudip Mukherjee
  2019-11-20 15:17 ` [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition Sudip Mukherjee
  0 siblings, 1 reply; 5+ messages in thread
From: Sudip Mukherjee @ 2019-11-20 15:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Rob Herring
  Cc: linux-kernel, linux-serial, Sudip Mukherjee

The argument 'inode' passed to tty_open_by_driver() was not being used.
Remove the extra argument.

Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
---
 drivers/tty/tty_io.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index a81807b394d1..cb6370906a6d 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1925,7 +1925,6 @@ EXPORT_SYMBOL_GPL(tty_kopen);
 /**
  *	tty_open_by_driver	-	open a tty device
  *	@device: dev_t of device to open
- *	@inode: inode of device file
  *	@filp: file pointer to tty
  *
  *	Performs the driver lookup, checks for a reopen, or otherwise
@@ -1938,7 +1937,7 @@ EXPORT_SYMBOL_GPL(tty_kopen);
  *	  - concurrent tty driver removal w/ lookup
  *	  - concurrent tty removal from driver table
  */
-static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
+static struct tty_struct *tty_open_by_driver(dev_t device,
 					     struct file *filp)
 {
 	struct tty_struct *tty;
@@ -2030,7 +2029,7 @@ static int tty_open(struct inode *inode, struct file *filp)
 
 	tty = tty_open_current_tty(device, filp);
 	if (!tty)
-		tty = tty_open_by_driver(device, inode, filp);
+		tty = tty_open_by_driver(device, filp);
 
 	if (IS_ERR(tty)) {
 		tty_free_file(filp);
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition
  2019-11-20 15:17 [PATCH 1/2] tty: remove unused argument from tty_open_by_driver() Sudip Mukherjee
@ 2019-11-20 15:17 ` Sudip Mukherjee
  2019-11-20 15:44   ` Greg Kroah-Hartman
  2019-11-25  6:27   ` Dan Carpenter
  0 siblings, 2 replies; 5+ messages in thread
From: Sudip Mukherjee @ 2019-11-20 15:17 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Rob Herring
  Cc: linux-kernel, linux-serial, Sudip Mukherjee

There seems to be a race condition in tty drivers and I could see on
many boot cycles a NULL pointer dereference as tty_init_dev() tries to
do 'tty->port->itty = tty' even though tty->port is NULL.
'tty->port' will be set by the driver and if the driver has not yet done
it before we open the tty device we can get to this situation. By adding
some extra debug prints, I noticed that tty_port_link_device() is
initialising 'driver->ports[index]' just few microseconds after I
get the warning.
So, add one retry so that tty_init_dev() will return -EAGAIN on its first
try if 'tty->port' is not set yet, and then tty_open() will try to open
it again.

Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
---
 drivers/tty/pty.c                   |  2 +-
 drivers/tty/serdev/serdev-ttyport.c |  2 +-
 drivers/tty/tty_io.c                | 20 ++++++++++++++------
 include/linux/tty.h                 |  3 ++-
 4 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 00099a8439d2..22e8c40d9f9c 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -842,7 +842,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 
 
 	mutex_lock(&tty_mutex);
-	tty = tty_init_dev(ptm_driver, index);
+	tty = tty_init_dev(ptm_driver, index, 0);
 	/* The tty returned here is locked so we can safely
 	   drop the mutex */
 	mutex_unlock(&tty_mutex);
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index d1cdd2ab8b4c..1162b4202e80 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -109,7 +109,7 @@ static int ttyport_open(struct serdev_controller *ctrl)
 	struct ktermios ktermios;
 	int ret;
 
-	tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
+	tty = tty_init_dev(serport->tty_drv, serport->tty_idx, 0);
 	if (IS_ERR(tty))
 		return PTR_ERR(tty);
 	serport->tty = tty;
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index cb6370906a6d..e1b2086317fb 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1295,6 +1295,7 @@ static int tty_reopen(struct tty_struct *tty)
  *	tty_init_dev		-	initialise a tty device
  *	@driver: tty driver we are opening a device on
  *	@idx: device index
+ *	@retry: retry count if driver has not set tty->port yet
  *	@ret_tty: returned tty structure
  *
  *	Prepare a tty device. This may not be a "new" clean device but
@@ -1315,7 +1316,8 @@ static int tty_reopen(struct tty_struct *tty)
  * relaxed for the (most common) case of reopening a tty.
  */
 
-struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
+struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
+				int retry)
 {
 	struct tty_struct *tty;
 	int retval;
@@ -1344,6 +1346,10 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
 
 	if (!tty->port)
 		tty->port = driver->ports[idx];
+	if (!tty->port && retry) {
+		retval = -EAGAIN;
+		goto err_release_driver;
+	}
 
 	WARN_RATELIMIT(!tty->port,
 			"%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
@@ -1366,6 +1372,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_release_driver:
+	tty_driver_remove_tty(driver, tty);
 err_free_tty:
 	tty_unlock(tty);
 	free_tty_struct(tty);
@@ -1910,7 +1918,7 @@ struct tty_struct *tty_kopen(dev_t device)
 		tty_kref_put(tty);
 		tty = ERR_PTR(-EBUSY);
 	} else { /* tty_init_dev returns tty with the tty_lock held */
-		tty = tty_init_dev(driver, index);
+		tty = tty_init_dev(driver, index, 0);
 		if (IS_ERR(tty))
 			goto out;
 		tty_port_set_kopened(tty->port, 1);
@@ -1937,7 +1945,7 @@ EXPORT_SYMBOL_GPL(tty_kopen);
  *	  - concurrent tty driver removal w/ lookup
  *	  - concurrent tty removal from driver table
  */
-static struct tty_struct *tty_open_by_driver(dev_t device,
+static struct tty_struct *tty_open_by_driver(dev_t device, int retry,
 					     struct file *filp)
 {
 	struct tty_struct *tty;
@@ -1981,7 +1989,7 @@ static struct tty_struct *tty_open_by_driver(dev_t device,
 			tty = ERR_PTR(retval);
 		}
 	} else { /* Returns with the tty_lock held for now */
-		tty = tty_init_dev(driver, index);
+		tty = tty_init_dev(driver, index, retry);
 		mutex_unlock(&tty_mutex);
 	}
 out:
@@ -2016,7 +2024,7 @@ static struct tty_struct *tty_open_by_driver(dev_t device,
 static int tty_open(struct inode *inode, struct file *filp)
 {
 	struct tty_struct *tty;
-	int noctty, retval;
+	int noctty, retval, retry = 1;
 	dev_t device = inode->i_rdev;
 	unsigned saved_flags = filp->f_flags;
 
@@ -2029,7 +2037,7 @@ static int tty_open(struct inode *inode, struct file *filp)
 
 	tty = tty_open_current_tty(device, filp);
 	if (!tty)
-		tty = tty_open_by_driver(device, filp);
+		tty = tty_open_by_driver(device, retry--, filp);
 
 	if (IS_ERR(tty)) {
 		tty_free_file(filp);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index bfa4e2ee94a9..2f74fc138e6a 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -559,7 +559,8 @@ extern struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx);
 extern int tty_alloc_file(struct file *file);
 extern void tty_add_file(struct tty_struct *tty, struct file *file);
 extern void tty_free_file(struct file *file);
-extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx);
+extern struct tty_struct *tty_init_dev(struct tty_driver *driver,
+				       int idx, int retry);
 extern void tty_release_struct(struct tty_struct *tty, int idx);
 extern int tty_release(struct inode *inode, struct file *filp);
 extern void tty_init_termios(struct tty_struct *tty);
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition
  2019-11-20 15:17 ` [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition Sudip Mukherjee
@ 2019-11-20 15:44   ` Greg Kroah-Hartman
  2019-11-20 15:55     ` Sudip Mukherjee
  2019-11-25  6:27   ` Dan Carpenter
  1 sibling, 1 reply; 5+ messages in thread
From: Greg Kroah-Hartman @ 2019-11-20 15:44 UTC (permalink / raw)
  To: Sudip Mukherjee; +Cc: Jiri Slaby, Rob Herring, linux-kernel, linux-serial

On Wed, Nov 20, 2019 at 03:17:09PM +0000, Sudip Mukherjee wrote:
> There seems to be a race condition in tty drivers and I could see on
> many boot cycles a NULL pointer dereference as tty_init_dev() tries to
> do 'tty->port->itty = tty' even though tty->port is NULL.
> 'tty->port' will be set by the driver and if the driver has not yet done
> it before we open the tty device we can get to this situation. By adding
> some extra debug prints, I noticed that tty_port_link_device() is
> initialising 'driver->ports[index]' just few microseconds after I
> get the warning.
> So, add one retry so that tty_init_dev() will return -EAGAIN on its first
> try if 'tty->port' is not set yet, and then tty_open() will try to open
> it again.
> 
> Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
> ---
>  drivers/tty/pty.c                   |  2 +-
>  drivers/tty/serdev/serdev-ttyport.c |  2 +-
>  drivers/tty/tty_io.c                | 20 ++++++++++++++------
>  include/linux/tty.h                 |  3 ++-
>  4 files changed, 18 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
> index 00099a8439d2..22e8c40d9f9c 100644
> --- a/drivers/tty/pty.c
> +++ b/drivers/tty/pty.c
> @@ -842,7 +842,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
>  
>  
>  	mutex_lock(&tty_mutex);
> -	tty = tty_init_dev(ptm_driver, index);
> +	tty = tty_init_dev(ptm_driver, index, 0);

Horrible naming scheme for this new "flag".

Look at that call here, can you instantly tell what this call is doing
with "0"?  I sure can not :(

If you really want to do this, you make a different function,
tty_init_dev_retry() and then have that pass in a retry flag in the tty
core, so that any users always know what they are doing here.

But, this really feels like a race in the code somewhere:

> --- a/drivers/tty/tty_io.c
> +++ b/drivers/tty/tty_io.c
> @@ -1295,6 +1295,7 @@ static int tty_reopen(struct tty_struct *tty)
>   *	tty_init_dev		-	initialise a tty device
>   *	@driver: tty driver we are opening a device on
>   *	@idx: device index
> + *	@retry: retry count if driver has not set tty->port yet

Why would tty->port not be set up already?  The caller has control over
this, what is not happening correctly to cause this?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition
  2019-11-20 15:44   ` Greg Kroah-Hartman
@ 2019-11-20 15:55     ` Sudip Mukherjee
  0 siblings, 0 replies; 5+ messages in thread
From: Sudip Mukherjee @ 2019-11-20 15:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Jiri Slaby, Rob Herring, linux-kernel, linux-serial

On Wed, Nov 20, 2019 at 04:44:50PM +0100, Greg Kroah-Hartman wrote:
> On Wed, Nov 20, 2019 at 03:17:09PM +0000, Sudip Mukherjee wrote:
> > There seems to be a race condition in tty drivers and I could see on
> > many boot cycles a NULL pointer dereference as tty_init_dev() tries to
> > do 'tty->port->itty = tty' even though tty->port is NULL.
<snip>
> > +++ b/drivers/tty/pty.c
> > @@ -842,7 +842,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
> >  
> >  
> >  	mutex_lock(&tty_mutex);
> > -	tty = tty_init_dev(ptm_driver, index);
> > +	tty = tty_init_dev(ptm_driver, index, 0);
> 
> Horrible naming scheme for this new "flag".
> 
> Look at that call here, can you instantly tell what this call is doing
> with "0"?  I sure can not :(

well, I also made the mistake of 1->0 in my initial patch. :(

> 
> If you really want to do this, you make a different function,
> tty_init_dev_retry() and then have that pass in a retry flag in the tty
> core, so that any users always know what they are doing here.

will do.

> 
> But, this really feels like a race in the code somewhere:
> 
> > --- a/drivers/tty/tty_io.c
> > +++ b/drivers/tty/tty_io.c
> > @@ -1295,6 +1295,7 @@ static int tty_reopen(struct tty_struct *tty)
> >   *	tty_init_dev		-	initialise a tty device
> >   *	@driver: tty driver we are opening a device on
> >   *	@idx: device index
> > + *	@retry: retry count if driver has not set tty->port yet
> 
> Why would tty->port not be set up already?  The caller has control over
> this, what is not happening correctly to cause this?

Will add more debugs to check what is happening now and then send you v2.


--
Regards
Sudip

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition
  2019-11-20 15:17 ` [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition Sudip Mukherjee
  2019-11-20 15:44   ` Greg Kroah-Hartman
@ 2019-11-25  6:27   ` Dan Carpenter
  1 sibling, 0 replies; 5+ messages in thread
From: Dan Carpenter @ 2019-11-25  6:27 UTC (permalink / raw)
  To: kbuild, Sudip Mukherjee
  Cc: kbuild-all, Greg Kroah-Hartman, Jiri Slaby, Rob Herring,
	linux-kernel, linux-serial, Sudip Mukherjee

Hi Sudip,

[auto build test WARNING on v5.4-rc8]
[cannot apply to tty/tty-testing next-20191122]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Sudip-Mukherjee/tty-remove-unused-argument-from-tty_open_by_driver/20191123-164153
base:    af42d3466bdc8f39806b26f593604fdc54140bcb

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
drivers/tty/tty_io.c:1360 tty_init_dev() error: we previously assumed 'tty->port' could be null (see line 1348)

# https://github.com/0day-ci/linux/commit/8de47da07f8c6fe6f631965cafb384cd0d72ce40
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 8de47da07f8c6fe6f631965cafb384cd0d72ce40
vim +1360 drivers/tty/tty_io.c

8de47da07f8c6f drivers/tty/tty_io.c  Sudip Mukherjee     2019-11-20  1318  struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
8de47da07f8c6f drivers/tty/tty_io.c  Sudip Mukherjee     2019-11-20  1319  				int retry)
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1320  {
bf970ee46e0fb3 drivers/char/tty_io.c Alan Cox            2008-10-13  1321  	struct tty_struct *tty;
73ec06fc5f5c8e drivers/char/tty_io.c Alan Cox            2008-10-13  1322  	int retval;
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1323  
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1324  	/*
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1325  	 * First time open is complex, especially for PTY devices.
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1326  	 * This code guarantees that either everything succeeds and the
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1327  	 * TTY is ready for operation, or else the table slots are vacated
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1328  	 * and the allocated memory released.  (Except that the termios
16b00ae82dce0e drivers/tty/tty_io.c  Johan Hovold        2017-03-30  1329  	 * may be retained.)
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1330  	 */
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1331  
73ec06fc5f5c8e drivers/char/tty_io.c Alan Cox            2008-10-13  1332  	if (!try_module_get(driver->owner))
73ec06fc5f5c8e drivers/char/tty_io.c Alan Cox            2008-10-13  1333  		return ERR_PTR(-ENODEV);
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1334  
2c964a2f4191f2 drivers/tty/tty_io.c  Rasmus Villemoes    2014-07-10  1335  	tty = alloc_tty_struct(driver, idx);
d5543503753983 drivers/tty/tty_io.c  Jiri Slaby          2011-03-23  1336  	if (!tty) {
d5543503753983 drivers/tty/tty_io.c  Jiri Slaby          2011-03-23  1337  		retval = -ENOMEM;
d5543503753983 drivers/tty/tty_io.c  Jiri Slaby          2011-03-23  1338  		goto err_module_put;
d5543503753983 drivers/tty/tty_io.c  Jiri Slaby          2011-03-23  1339  	}
^1da177e4c3f41 drivers/char/tty_io.c Linus Torvalds      2005-04-16  1340  
89c8d91e31f267 drivers/tty/tty_io.c  Alan Cox            2012-08-08  1341  	tty_lock(tty);
73ec06fc5f5c8e drivers/char/tty_io.c Alan Cox            2008-10-13  1342  	retval = tty_driver_install_tty(driver, tty);
d5543503753983 drivers/tty/tty_io.c  Jiri Slaby          2011-03-23  1343  	if (retval < 0)
c8b710b3e43481 drivers/tty/tty_io.c  Peter Hurley        2016-01-09  1344  		goto err_free_tty;
8b0a88d5912ab5 drivers/char/tty_io.c Alan Cox            2008-10-13  1345  
04831dc154df9b drivers/tty/tty_io.c  Jiri Slaby          2012-06-04  1346  	if (!tty->port)
04831dc154df9b drivers/tty/tty_io.c  Jiri Slaby          2012-06-04  1347  		tty->port = driver->ports[idx];
8de47da07f8c6f drivers/tty/tty_io.c  Sudip Mukherjee     2019-11-20 @1348  	if (!tty->port && retry) {
                                                                                    ^^^^^^^^^^
Check

8de47da07f8c6f drivers/tty/tty_io.c  Sudip Mukherjee     2019-11-20  1349  		retval = -EAGAIN;
8de47da07f8c6f drivers/tty/tty_io.c  Sudip Mukherjee     2019-11-20  1350  		goto err_release_driver;
8de47da07f8c6f drivers/tty/tty_io.c  Sudip Mukherjee     2019-11-20  1351  	}
04831dc154df9b drivers/tty/tty_io.c  Jiri Slaby          2012-06-04  1352  
5d4121c04b3577 drivers/tty/tty_io.c  Jiri Slaby          2012-08-17  1353  	WARN_RATELIMIT(!tty->port,
5d4121c04b3577 drivers/tty/tty_io.c  Jiri Slaby          2012-08-17  1354  			"%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
5d4121c04b3577 drivers/tty/tty_io.c  Jiri Slaby          2012-08-17  1355  			__func__, tty->driver->name);
5d4121c04b3577 drivers/tty/tty_io.c  Jiri Slaby          2012-08-17  1356  
b027e2298bd588 drivers/tty/tty_io.c  Gaurav Kohli        2018-01-23  1357  	retval = tty_ldisc_lock(tty, 5 * HZ);
b027e2298bd588 drivers/tty/tty_io.c  Gaurav Kohli        2018-01-23  1358  	if (retval)
b027e2298bd588 drivers/tty/tty_io.c  Gaurav Kohli        2018-01-23  1359  		goto err_release_lock;
967fab6916681e drivers/tty/tty_io.c  Jiri Slaby          2012-10-18 @1360  	tty->port->itty = tty;
                                                                                ^^^^^^^^^^^^^^^^^^^^^^
Unchecked dereference.

967fab6916681e drivers/tty/tty_io.c  Jiri Slaby          2012-10-18  1361  

---
0-DAY kernel test infrastructure                 Open Source Technology Center
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org Intel Corporation

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2019-11-25  6:29 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-20 15:17 [PATCH 1/2] tty: remove unused argument from tty_open_by_driver() Sudip Mukherjee
2019-11-20 15:17 ` [PATCH 2/2] tty: add retry to tty_init_dev() to workaround a race condition Sudip Mukherjee
2019-11-20 15:44   ` Greg Kroah-Hartman
2019-11-20 15:55     ` Sudip Mukherjee
2019-11-25  6:27   ` Dan Carpenter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).