2018-09-03 16:59:06

by Dmitry Safonov

[permalink] [raw]
Subject: [PATCHv2 0/4] tty: Hold write ldisc sem in tty_reopen()

The first two fixes are worth to have in stables as we've hit it
on v4.9 stable.

And for linux-next - adding lockdep asserts for line discipline changing
code, verifying that write ldisc sem will be held forthwith.

Changes since v1:
- Added tested-by/reported-by tags
- Dropped 3/4 (locking tty pair for lockdep sake),
Because of that - not adding lockdep_assert_held() in tty_ldisc_open()
- Added 4/4 cleanup to inc tty->count only on success of
tty_ldisc_reinit()
- lock ldisc without (5*HZ) timeout in tty_reopen()

v1 link:
lkml.kernel.org/r/<[email protected]>

Huuge cc list:
Cc: Daniel Axtens <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Michael Neuling <[email protected]>
Cc: Mikulas Patocka <[email protected]>
Cc: Nathan March <[email protected]>
Cc: Pasi Kärkkäinen <[email protected]>
Cc: Peter Hurley <[email protected]>
Cc: Sergey Senozhatsky <[email protected]>
Cc: Tan Xiaojun <[email protected]>
Cc: Tetsuo Handa <[email protected]>
(please, ignore if I Cc'ed you mistakenly)

Dmitry Safonov (4):
tty: Drop tty->count on tty_reopen() failure
tty: Hold tty_ldisc_lock() during tty_reopen()
tty/lockdep: Add ldisc_sem asserts
tty: Simplify tty->count math in tty_reopen()

drivers/tty/tty_io.c | 12 ++++++++----
drivers/tty/tty_ldisc.c | 5 +++++
2 files changed, 13 insertions(+), 4 deletions(-)

--
2.13.6



2018-09-03 16:55:15

by Dmitry Safonov

[permalink] [raw]
Subject: [PATCHv2 4/4] tty: Simplify tty->count math in tty_reopen()

As noted by Jiri, tty_ldisc_reinit() shouldn't rely on tty counter.
Simplify math by increasing the counter after reinit success.

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Link: lkml.kernel.org/r/<[email protected]>
Suggested-by: Jiri Slaby <[email protected]>
Signed-off-by: Dmitry Safonov <[email protected]>
---
drivers/tty/tty_io.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index a947719b4626..7f968ac14bbd 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1268,17 +1268,13 @@ static int tty_reopen(struct tty_struct *tty)
return -EBUSY;

tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT);
+ if (!tty->ldisc)
+ retval = tty_ldisc_reinit(tty, tty->termios.c_line);
+ tty_ldisc_unlock(tty);

- tty->count++;
- if (tty->ldisc)
- goto out_unlock;
+ if (retval == 0)
+ tty->count++;

- retval = tty_ldisc_reinit(tty, tty->termios.c_line);
- if (retval)
- tty->count--;
-
-out_unlock:
- tty_ldisc_unlock(tty);
return retval;
}

--
2.13.6


2018-09-03 16:58:22

by Dmitry Safonov

[permalink] [raw]
Subject: [PATCHv2 3/4] tty/lockdep: Add ldisc_sem asserts

Make sure under CONFIG_LOCKDEP that each change to line discipline
is done with held write semaphor.
Otherwise potential reader will have a good time dereferencing
incomplete/uninitialized ldisc.

Exception here is tty_ldisc_open(), as it's called without ldisc_sem
locked by tty_init_dev() for the tty->link.

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Signed-off-by: Dmitry Safonov <[email protected]>
---
drivers/tty/tty_ldisc.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index fc4c97cae01e..202cb645582f 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -471,6 +471,7 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)

static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
{
+ lockdep_assert_held(&tty->ldisc_sem);
WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags));
clear_bit(TTY_LDISC_OPEN, &tty->flags);
if (ld->ops->close)
@@ -492,6 +493,7 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld)
struct tty_ldisc *disc = tty_ldisc_get(tty, ld);
int r;

+ lockdep_assert_held(&tty->ldisc_sem);
if (IS_ERR(disc))
return PTR_ERR(disc);
tty->ldisc = disc;
@@ -615,6 +617,7 @@ EXPORT_SYMBOL_GPL(tty_set_ldisc);
*/
static void tty_ldisc_kill(struct tty_struct *tty)
{
+ lockdep_assert_held(&tty->ldisc_sem);
if (!tty->ldisc)
return;
/*
@@ -662,6 +665,7 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
struct tty_ldisc *ld;
int retval;

+ lockdep_assert_held(&tty->ldisc_sem);
ld = tty_ldisc_get(tty, disc);
if (IS_ERR(ld)) {
BUG_ON(disc == N_TTY);
@@ -825,6 +829,7 @@ int tty_ldisc_init(struct tty_struct *tty)
*/
void tty_ldisc_deinit(struct tty_struct *tty)
{
+ /* no ldisc_sem, tty is being destroyed */
if (tty->ldisc)
tty_ldisc_put(tty->ldisc);
tty->ldisc = NULL;
--
2.13.6


2018-09-03 16:58:54

by Dmitry Safonov

[permalink] [raw]
Subject: [PATCHv2 1/4] tty: Drop tty->count on tty_reopen() failure

In case of tty_ldisc_reinit() failure, tty->count should be decremented
back, otherwise we will never release_tty().
Tetsuo reported that it fixes noisy warnings on tty release like:
pts pts4033: tty_release: tty->count(10529) != (#fd's(7) + #kopen's(0))

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Cc: [email protected] # v4.6+
Fixes: commit 892d1fa7eaae ("tty: Destroy ldisc instance on hangup")
Tested-by: Jiri Slaby <[email protected]>
Tested-by: Tetsuo Handa <[email protected]>
Signed-off-by: Dmitry Safonov <[email protected]>
---
drivers/tty/tty_io.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 32bc3e3fe4d3..5e5da9acaf0a 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1255,6 +1255,7 @@ static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *
static int tty_reopen(struct tty_struct *tty)
{
struct tty_driver *driver = tty->driver;
+ int retval;

if (driver->type == TTY_DRIVER_TYPE_PTY &&
driver->subtype == PTY_TYPE_MASTER)
@@ -1268,10 +1269,14 @@ static int tty_reopen(struct tty_struct *tty)

tty->count++;

- if (!tty->ldisc)
- return tty_ldisc_reinit(tty, tty->termios.c_line);
+ if (tty->ldisc)
+ return 0;

- return 0;
+ retval = tty_ldisc_reinit(tty, tty->termios.c_line);
+ if (retval)
+ tty->count--;
+
+ return retval;
}

/**
--
2.13.6


2018-09-03 16:58:59

by Dmitry Safonov

[permalink] [raw]
Subject: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()

tty_ldisc_reinit() doesn't race with neither tty_ldisc_hangup()
nor set_ldisc() nor tty_ldisc_release() as they use tty lock.
But it races with anyone who expects line discipline to be the same
after hoding read semaphore in tty_ldisc_ref().

We've seen the following crash on v4.9.108 stable:

BUG: unable to handle kernel paging request at 0000000000002260
IP: [..] n_tty_receive_buf_common+0x5f/0x86d
Workqueue: events_unbound flush_to_ldisc
Call Trace:
[..] n_tty_receive_buf2
[..] tty_ldisc_receive_buf
[..] flush_to_ldisc
[..] process_one_work
[..] worker_thread
[..] kthread
[..] ret_from_fork

tty_ldisc_reinit() should be called with ldisc_sem hold for writing,
which will protect any reader against line discipline changes.

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Cc: [email protected] # depends on commit b027e2298bd5 ("tty: fix
data race between tty_init_dev and flush of buf")
Reported-by: [email protected]
Tested-by: Tetsuo Handa <[email protected]>
Signed-off-by: Dmitry Safonov <[email protected]>
---
drivers/tty/tty_io.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 5e5da9acaf0a..a947719b4626 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1255,7 +1255,7 @@ static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *
static int tty_reopen(struct tty_struct *tty)
{
struct tty_driver *driver = tty->driver;
- int retval;
+ int retval = 0;

if (driver->type == TTY_DRIVER_TYPE_PTY &&
driver->subtype == PTY_TYPE_MASTER)
@@ -1267,15 +1267,18 @@ static int tty_reopen(struct tty_struct *tty)
if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
return -EBUSY;

- tty->count++;
+ tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT);

+ tty->count++;
if (tty->ldisc)
- return 0;
+ goto out_unlock;

retval = tty_ldisc_reinit(tty, tty->termios.c_line);
if (retval)
tty->count--;

+out_unlock:
+ tty_ldisc_unlock(tty);
return retval;
}

--
2.13.6


2018-09-04 01:53:17

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()

On (09/03/18 17:52), Dmitry Safonov wrote:
>
> We've seen the following crash on v4.9.108 stable:
>
> BUG: unable to handle kernel paging request at 0000000000002260
> IP: [..] n_tty_receive_buf_common+0x5f/0x86d
> Workqueue: events_unbound flush_to_ldisc
> Call Trace:
> [..] n_tty_receive_buf2
> [..] tty_ldisc_receive_buf
> [..] flush_to_ldisc
> [..] process_one_work
> [..] worker_thread
> [..] kthread
> [..] ret_from_fork
>
> tty_ldisc_reinit() should be called with ldisc_sem hold for writing,
> which will protect any reader against line discipline changes.
>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Jiri Slaby <[email protected]>
> Cc: [email protected] # depends on commit b027e2298bd5 ("tty: fix
> data race between tty_init_dev and flush of buf")

I believe there's a "Fixes" tag for that

Fixes: b027e2298bd5 ("tty: fix data race between tty_init_dev and flush of buf")
Cc: [email protected]

-ss

2018-09-04 06:32:18

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()

On 09/04/2018, 03:51 AM, Sergey Senozhatsky wrote:
> On (09/03/18 17:52), Dmitry Safonov wrote:
>>
>> We've seen the following crash on v4.9.108 stable:
>>
>> BUG: unable to handle kernel paging request at 0000000000002260
>> IP: [..] n_tty_receive_buf_common+0x5f/0x86d
>> Workqueue: events_unbound flush_to_ldisc
>> Call Trace:
>> [..] n_tty_receive_buf2
>> [..] tty_ldisc_receive_buf
>> [..] flush_to_ldisc
>> [..] process_one_work
>> [..] worker_thread
>> [..] kthread
>> [..] ret_from_fork
>>
>> tty_ldisc_reinit() should be called with ldisc_sem hold for writing,
>> which will protect any reader against line discipline changes.
>>
>> Cc: Greg Kroah-Hartman <[email protected]>
>> Cc: Jiri Slaby <[email protected]>
>> Cc: [email protected] # depends on commit b027e2298bd5 ("tty: fix
>> data race between tty_init_dev and flush of buf")
>
> I believe there's a "Fixes" tag for that
>
> Fixes: b027e2298bd5 ("tty: fix data race between tty_init_dev and flush of buf")
> Cc: [email protected]

Nope, it would be translated as:
Backport-first: b027e2298bd5
:)

thanks,
--
js
suse labs

2018-09-04 07:07:44

by Sergey Senozhatsky

[permalink] [raw]
Subject: Re: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()

On (09/04/18 08:30), Jiri Slaby wrote:
>
> >> Cc: [email protected] # depends on commit b027e2298bd5 ("tty: fix
> >> data race between tty_init_dev and flush of buf")
> >
> > I believe there's a "Fixes" tag for that
> >
> > Fixes: b027e2298bd5 ("tty: fix data race between tty_init_dev and flush of buf")
> > Cc: [email protected]
>
> Nope, it would be translated as:
> Backport-first: b027e2298bd5
> :)

:) Ah, OK.
So this "Backport-first:" is for pre-4.9 longterm kernels.
As far as I can see linux-4.9.y picked up b027e2298bd5:

commit 55eaecffe3d663d02084023b9fc06d5f39b97389
Author: Gaurav Kohli
Date: Tue Jan 23 13:16:34 2018 +0530

tty: fix data race between tty_init_dev and flush of buf

commit b027e2298bd588d6fa36ed2eda97447fb3eac078 upstream.

-ss

2018-09-04 09:00:18

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCHv2 1/4] tty: Drop tty->count on tty_reopen() failure

On 09/03/2018, 06:52 PM, Dmitry Safonov wrote:
> In case of tty_ldisc_reinit() failure, tty->count should be decremented
> back, otherwise we will never release_tty().
> Tetsuo reported that it fixes noisy warnings on tty release like:
> pts pts4033: tty_release: tty->count(10529) != (#fd's(7) + #kopen's(0))
>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Jiri Slaby <[email protected]>
> Cc: [email protected] # v4.6+
> Fixes: commit 892d1fa7eaae ("tty: Destroy ldisc instance on hangup")
> Tested-by: Jiri Slaby <[email protected]>

Reviewed-by: Jiri Slaby <[email protected]>

> Tested-by: Tetsuo Handa <[email protected]>
> Signed-off-by: Dmitry Safonov <[email protected]>

thanks,
--
js
suse labs

2018-09-04 09:03:42

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()

On 09/03/2018, 06:52 PM, Dmitry Safonov wrote:
> tty_ldisc_reinit() doesn't race with neither tty_ldisc_hangup()
> nor set_ldisc() nor tty_ldisc_release() as they use tty lock.
> But it races with anyone who expects line discipline to be the same
> after hoding read semaphore in tty_ldisc_ref().
>
> We've seen the following crash on v4.9.108 stable:
>
> BUG: unable to handle kernel paging request at 0000000000002260
> IP: [..] n_tty_receive_buf_common+0x5f/0x86d
> Workqueue: events_unbound flush_to_ldisc
> Call Trace:
> [..] n_tty_receive_buf2
> [..] tty_ldisc_receive_buf
> [..] flush_to_ldisc
> [..] process_one_work
> [..] worker_thread
> [..] kthread
> [..] ret_from_fork
>
> tty_ldisc_reinit() should be called with ldisc_sem hold for writing,
> which will protect any reader against line discipline changes.
>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Jiri Slaby <[email protected]>

Reviewed-by: Jiri Slaby <[email protected]>

> Cc: [email protected] # depends on commit b027e2298bd5 ("tty: fix
> data race between tty_init_dev and flush of buf")
> Reported-by: [email protected]
> Tested-by: Tetsuo Handa <[email protected]>
> Signed-off-by: Dmitry Safonov <[email protected]>

thanks,
--
js
suse labs

2018-09-04 09:05:03

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCHv2 4/4] tty: Simplify tty->count math in tty_reopen()

On 09/03/2018, 06:52 PM, Dmitry Safonov wrote:
> As noted by Jiri, tty_ldisc_reinit() shouldn't rely on tty counter.
> Simplify math by increasing the counter after reinit success.
>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Jiri Slaby <[email protected]>

Reviewed-by: Jiri Slaby <[email protected]>

> Link: lkml.kernel.org/r/<[email protected]>
> Suggested-by: Jiri Slaby <[email protected]>
> Signed-off-by: Dmitry Safonov <[email protected]>

thanks,
--
js
suse labs