tty: Hold write ldisc sem in tty_reopen()
Subsystems (2)
v2 cover 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(-)
Signature · 1 line
2.13.6
PATCH v2 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))
--- drivers/tty/tty_io.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/tty/tty_io.c +8 -3
@@ -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; }/**
Signature · 1 line
2.13.6
Re: [PATCHv2 1/4] tty: Drop tty->count on tty_reopen() failure
Quoted 10 lines · Dmitry Safonov
In case of
tty_ldisc_reinit()failure,tty->countshould be decremented back, otherwise we will neverrelease_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]>
Quoted 2 lines
Tested-by Tetsuo Handa <[email protected]>Signed-off-by Dmitry Safonov <[email protected]>
thanks,
Signature · 2 lines
js suse labs
PATCH v2 2/4 tty: Hold tty_ldisc_lock() during tty_reopen()
In reply to Dmitry Safonovtty_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")
--- drivers/tty/tty_io.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/tty/tty_io.c +6 -3
@@ -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; }
Signature · 1 line
2.13.6
Re: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()
Quoted 15 lines · Dmitry Safonov
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
Quoted 7 lines
tty_ldisc_reinit()should be called withldisc_semhold 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 commitb027e2298bd5("tty: fix data race betweentty_init_devand flush of buf")
I believe there's a "Fixes" tag for that
-ss
Re: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()
Quoted 24 lines · Sergey Senozhatsky
Quoted 15 lines · Dmitry Safonov
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_forkQuoted 7 lines
tty_ldisc_reinit()should be called withldisc_semhold 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 commitb027e2298bd5("tty: fix data race betweentty_init_devand flush of buf")
Quoted 4 lines
I believe there's a "Fixes" tag for that
Fixes b027e2298bd5 ("tty: fix data race between tty_init_dev and flush of buf")
Nope, it would be translated as:
Backport-first: b027e2298bd5
:)
thanks,
Signature · 2 lines
js suse labs
Re: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()
Quoted 4 lines · Jiri Slaby
Quoted 4 lines
Quoted 3 lines
Cc: [email protected] # depends on commit
b027e2298bd5("tty: fix data race betweentty_init_devand flush of buf")
Quoted 8 lines
Quoted 4 lines
I believe there's a "Fixes" tag for that
Fixes b027e2298bd5 ("tty: fix data race between tty_init_dev and flush of buf")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.
-ssRe: [PATCHv2 2/4] tty: Hold tty_ldisc_lock() during tty_reopen()
In reply to Dmitry SafonovQuoted 19 lines · Dmitry Safonov
tty_ldisc_reinit()doesn't race with neithertty_ldisc_hangup()norset_ldisc()nortty_ldisc_release()as they use tty lock. But it races with anyone who expects line discipline to be the same after hoding read semaphore intty_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
Quoted 5 lines
tty_ldisc_reinit()should be called withldisc_semhold for writing, which will protect any reader against line discipline changes.Cc Greg Kroah-Hartman <[email protected]>Cc Jiri Slaby <[email protected]>
Quoted 5 lines
Cc: [email protected] # depends on commit
b027e2298bd5("tty: fix data race betweentty_init_devand flush of buf")Reported-by [email protected]Tested-by Tetsuo Handa <[email protected]>Signed-off-by Dmitry Safonov <[email protected]>
thanks,
Signature · 2 lines
js suse labs
PATCH v2 3/4 tty/lockdep: Add ldisc_sem asserts
In reply to Dmitry SafonovMake 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.
--- drivers/tty/tty_ldisc.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/tty/tty_ldisc.c +5 -0
@@ -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 ;
Signature · 1 line
2.13.6
PATCH v2 4/4 tty: Simplify tty->count math in tty_reopen()
In reply to Dmitry SafonovAs noted by Jiri, tty_ldisc_reinit() shouldn't rely on tty counter. Simplify math by increasing the counter after reinit success.
--- drivers/tty/tty_io.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-)
drivers/tty/tty_io.c +5 -9
@@ -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; }
Signature · 1 line
2.13.6
Re: [PATCHv2 4/4] tty: Simplify tty->count math in tty_reopen()
Quoted 5 lines · Dmitry Safonov
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]>
Quoted 3 lines
Link lkml.kernel.org/r/<[email protected]>Suggested-by Jiri Slaby <[email protected]>Signed-off-by Dmitry Safonov <[email protected]>
thanks,
Signature · 2 lines
js suse labs