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
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
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
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
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
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
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
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
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
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
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