tty: Hold write ldisc sem in tty_reopen()
Subsystems (2)
v5 cover tty: Hold write ldisc sem in tty_reopen()
Hi all,
Three fixes that worth to have in the @stable, as they were hit by different people, including Arista 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.
The last patch is an optional and probably, timeout can be dropped for read_lock(). I'll do it if everyone agrees.
(Or as per discussion with Peter in v3, just convert ldisc to a regular rwsem).
Thanks,
Dima
Changes since v4:
- back to lock ldisc with (5*HZ) timeout in tty_reopen()
(LKP report link: lkml.kernel.org/r/<[email protected]>)
- reordered 3/7 with 2/7 for LKP robot
Changes since v3:
- Added tested-by Mark Rutland (thanks!)
- Dropped patch with smp_wmb() - wrong idea
- lockdep_assert_held() should be actually lockdep_assert_held_exclusive()
- Described why tty_ldisc_open() can be called without ldisc_sem held for pty slave end (o_tty).
- Added Peter's patch for dropping self-made lockdep annotations
- Fix for a reader(s) of ldisc semaphore waiting for an active reader(s)
Changes since v2:
- Added reviewed-by tags
- Hopefully, fixed reported by 0-day issue.
- Added optional fix for wait_readers decrement
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]> v2 link: lkml.kernel.org/r/<[email protected]> v3 link: lkml.kernel.org/r/<[email protected]> v4 link: lkml.kernel.org/r/<[email protected]>
Cc: Daniel Axtens <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Mark Rutland <[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: Peter Zijlstra <[email protected]>
Cc: "Rong, Chen" <[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 (6):
tty: Drop tty->count on tty_reopen() failure tty/ldsem: Wake up readers after timed out down_write()
tty: Hold tty_ldisc_lock() during tty_reopen()
tty: Simplify tty->count math in tty_reopen() tty/ldsem: Add lockdep asserts for ldisc_sem tty/ldsem: Decrement wait_readers on timeouted down_read()
Peter Zijlstra (1):
tty/ldsem: Convert to regular lockdep annotations
drivers/tty/tty_io.c | 13 ++++++++--- drivers/tty/tty_ldisc.c | 9 +++++++ drivers/tty/tty_ldsem.c | 62 ++++++++++++++++++++----------------------------- 3 files changed, 44 insertions(+), 40 deletions(-)
Signature · 1 line
2.13.6
PATCH v5 1/7 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
Greg Kroah-Hartman
maintainerRe: [PATCHv5 1/7] tty: Drop tty->count on tty_reopen() failure
Quoted 7 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))Fixes commit 892d1fa7eaae ("tty: Destroy ldisc instance on hangup")
Minor nit, no need to have "commit" in this line, it's implied.
Also, no need to put an extra line here either.
I'll queue this patch up now, have some question about the rest...
thanks,
greg k-h
Re: [PATCHv5 1/7] tty: Drop tty->count on tty_reopen() failure
Quoted 12 lines · Greg Kroah-Hartman
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))Fixes commit 892d1fa7eaae ("tty: Destroy ldisc instance onhangup")
Quoted 5 lines
Minor nit, no need to have "commit" in this line, it's implied.
Also, no need to put an extra line here either.
I'll queue this patch up now, have some question about the rest...
Thanks, Greg, will have in mind further.
Signature · 1 line
Dima
PATCH v5 2/7 tty/ldsem: Wake up readers after timed out down_write()
In reply to Dmitry Safonovldsem_down_read() will sleep if there is pending writer in the queue. If the writer times out, readers in the queue should be woken up, otherwise they may miss a chance to acquire the semaphore until the last active reader will do ldsem_up_read().
There was a couple of reports where there was one active reader and other readers soft locked up:
Showing all locks held in the system:
2 locks held by khungtaskd/17:
#0: (rcu_read_lock){......}, at: watchdog+0x124/0x6d1
#1: (tasklist_lock){.+.+..}, at: debug_show_all_locks+0x72/0x2d3
2 locks held by askfirst/123:
#0: (&tty->ldisc_sem){.+.+.+}, at: ldsem_down_read+0x46/0x58
#1: (&ldata->atomic_read_lock){+.+...}, at: n_tty_read+0x115/0xbe4Prevent readers wait for active readers to release ldisc semaphore.
--- drivers/tty/tty_ldsem.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/tty/tty_ldsem.c +10 -0
@@ -293,6 +293,16 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)if (!locked)atomic_long_add_return (-LDSEM_WAIT_BIAS , &sem->count);list_del (&waiter.list); + +/* +* In case of timeout, wake up every reader who gave the right of way +* to writer. Prevent separation readers into two groups: +* one that helds semaphore and another that sleeps. +* (in case of no contention with a writer) +*/ +if (!locked &&list_empty (&sem->write_wait)) +__ldsem_wake_readers (sem); +raw_spin_unlock_irq (&sem->wait_lock);__set_current_state (TASK_RUNNING );
Signature · 1 line
2.13.6
Greg Kroah-Hartman
maintainerRe: [PATCHv5 2/7] tty/ldsem: Wake up readers after timed out down_write()
Quoted 17 lines · Dmitry Safonov
ldsem_down_read()will sleep if there is pending writer in the queue. If the writer times out, readers in the queue should be woken up, otherwise they may miss a chance to acquire the semaphore until the last active reader will doldsem_up_read().There was a couple of reports where there was one active reader and other readers soft locked up:
Showing all locks held in the system: 2 locks held by khungtaskd/17: #0: (rcu_read_lock){......}, at: watchdog+0x124/0x6d1 #1: (tasklist_lock){.+.+..}, at: debug_show_all_locks+0x72/0x2d3 2 locks held by askfirst/123: #0: (&tty->ldisc_sem){.+.+.+}, at: ldsem_down_read+0x46/0x58 #1: (&ldata->atomic_read_lock){+.+...}, at: n_tty_read+0x115/0xbe4Prevent readers wait for active readers to release ldisc semaphore.
Quoted 8 lines
Link lkml.kernel.org/r/<[email protected]>Link lkml.kernel.org/r/<20180907045041.GF1110@shao2-debian>Cc Greg Kroah-Hartman <[email protected]>Cc Jiri Slaby <[email protected]>Cc Peter Zijlstra <[email protected]>Reported-by kernel test robot <[email protected]>Signed-off-by Dmitry Safonov <[email protected]>---
Why isn't this ok for the stable trees?
thanks,
greg k-h
Re: [PATCHv5 2/7] tty/ldsem: Wake up readers after timed out down_write()
Hi Greg,
Quoted 32 lines · Greg Kroah-Hartman
Quoted 23 lines · Dmitry Safonov
ldsem_down_read()will sleep if there is pending writer in the queue.
If the writer times out, readers in the queue should be woken up, otherwise they may miss a chance to acquire the semaphore until the last
active reader will doldsem_up_read().There was a couple of reports where there was one active reader and other readers soft locked up:
Showing all locks held in the system: 2 locks held by khungtaskd/17: #0: (rcu_read_lock){......}, at: watchdog+0x124/0x6d1 #1: (tasklist_lock){.+.+..}, at: debug_show_all_locks+0x72/0x2d3 2 locks held by askfirst/123: #0: (&tty->ldisc_sem){.+.+.+}, at: ldsem_down_read+0x46/0x58 #1: (&ldata->atomic_read_lock){+.+...}, at: n_tty_read+0x115/0xbe4Prevent readers wait for active readers to release ldisc semaphore.
Link: lkml.kernel.org/r/<20171121132855.ajdv4k6swzhvktl6@wfg-t540p. sh.intel.com>
Quoted 7 lines
Link lkml.kernel.org/r/<20180907045041.GF1110@shao2-debian>Cc Greg Kroah-Hartman <[email protected]>Cc Jiri Slaby <[email protected]>Cc Peter Zijlstra <[email protected]>Reported-by kernel test robot <[email protected]>Signed-off-by Dmitry Safonov <[email protected]>---
Quoted 1 line
Why isn't this ok for the stable trees?
I guess it is ok for stables, but I would prefer to have an ack from Peter before that.
Peter, could you look if my analysis and fix are proper? I'll fill more confident about it going to stables if it had your ack.
Signature · 2 lines
Thanks,
DmitryPATCH v5 3/7 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.
Backport-first: b027e2298bd5 ("tty: fix data race between tty_init_dev and flush of buf")
Cc: [email protected]
--- drivers/tty/tty_io.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/tty/tty_io.c +7 -2
@@ -1267,15 +1267,20 @@ static int tty_reopen(struct tty_struct *tty)if (test_bit (TTY_EXCLUSIVE , &tty->flags) && !capable (CAP_SYS_ADMIN ))return -EBUSY ; - tty->count++; + retval =tty_ldisc_lock (tty,5 *HZ ); +if (retval) +return retval; + 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
Greg Kroah-Hartman
maintainerRe: [PATCHv5 3/7] tty: Hold tty_ldisc_lock() during tty_reopen()
Quoted 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.Backport-first:
b027e2298bd5("tty: fix data race betweentty_init_devand flush of buf")
What does this mean?
Does this require that patch for a stable patch? If so, just do:
Cc: [email protected] # b027e2298bd5 ("tty: fix data race between tty_init_dev and flush of buf")
in the signed-off-by area. The stable documentation should describe
this pretty well. If not, we can modify it to make it more obvious.
can you fix this up for the next resend of this series?
thanks,
greg k-h
Re: [PATCHv5 3/7] tty: Hold tty_ldisc_lock() during tty_reopen()
Quoted 30 lines · Greg Kroah-Hartman
Quoted 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_forkQuoted 7 lines
tty_ldisc_reinit()should be called withldisc_semhold for writing,
which will protect any reader against line discipline changes.Backport-first:
b027e2298bd5("tty: fix data race betweentty_init_dev
and flush of buf")What does this mean?
Quoted 7 lines
Does this require that patch for a stable patch? If so, just do:
Cc: [email protected] #b027e2298bd5("tty: fix data race
betweentty_init_devand flush of buf")
in the signed-off-by area. The stable documentation should describe this pretty well. If not, we can modify it to make it more obvious.can you fix this up for the next resend of this series?
Sure, sorry about that non-obvious tag.
Signature · 2 lines
Thanks,
DmitryPATCH v5 4/7 tty: Simplify tty->count math in tty_reopen()
In reply to Dmitry SafonovAs notted 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 | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
drivers/tty/tty_io.c +5 -8
@@ -1271,16 +1271,13 @@ static int tty_reopen(struct tty_struct *tty)if (retval)return retval; - tty->count++; -if (tty->ldisc) - goto out_unlock; +if (!tty->ldisc) + retval =tty_ldisc_reinit (tty, tty->termios.c_line); +tty_ldisc_unlock (tty); - retval =tty_ldisc_reinit (tty, tty->termios.c_line); -if (retval) - tty->count--; +if (retval ==0 ) + tty->count++; -out_unlock: -tty_ldisc_unlock (tty);return retval; }
Signature · 1 line
2.13.6
PATCH v5 5/7 tty/ldsem: Convert to regular lockdep annotations
In reply to Dmitry SafonovFrom: Peter Zijlstra <[email protected]>
For some reason ldsem has its own lockdep wrappers, make them go away.
--- drivers/tty/tty_ldsem.c | 51 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 37 deletions(-)
drivers/tty/tty_ldsem.c +14 -37
@@ -34,29 +34,6 @@#include <linux/sched/task.h> -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __acq (l, s, t, r, c, n, i) \ - lock_acquire(&(l)->dep_map, s, t, r, c, n, i) -# define __rel (l, n, i) \ - lock_release(&(l)->dep_map, n, i) -#define lockdep_acquire (l, s, t, i) __acq(l, s, t, 0, 1, NULL, i) -#define lockdep_acquire_nest (l, s, t, n, i) __acq(l, s, t, 0, 1, n, i) -#define lockdep_acquire_read (l, s, t, i) __acq(l, s, t, 1, 1, NULL, i) -#define lockdep_release (l, n, i) __rel(l, n, i) -#else -# define lockdep_acquire (l, s, t, i) do { } while (0) -# define lockdep_acquire_nest (l, s, t, n, i) do { } while (0) -# define lockdep_acquire_read (l, s, t, i) do { } while (0) -# define lockdep_release (l, n, i) do { } while (0) -#endif - -#ifdef CONFIG_LOCK_STAT -# define lock_stat (_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_) -#else -# define lock_stat (_lock, stat) do { } while (0) -#endif - -#if BITS_PER_LONG ==64 # define LDSEM_ACTIVE_MASK 0xffffffffL#else
@@ -320,17 +297,17 @@ static int __ldsem_down_read_nested(struct ld_semaphore *sem, {long count; -lockdep_acquire_read (sem, subclass,0 , _RET_IP_); +rwsem_acquire_read (&sem->dep_map, subclass,0 , _RET_IP_); count =atomic_long_add_return (LDSEM_READ_BIAS , &sem->count);if (count <=0 ) { -lock_stat (sem, contended); +lock_contended (&sem->dep_map, _RET_IP_);if (!down_read_failed (sem, count, timeout)) { -lockdep_release (sem,1 , _RET_IP_); +rwsem_release (&sem->dep_map,1 , _RET_IP_);return 0 ; } } -lock_stat (sem, acquired); +lock_acquired (&sem->dep_map, _RET_IP_);return 1 ; }
@@ -339,17 +316,17 @@ static int __ldsem_down_write_nested(struct ld_semaphore *sem, {long count; -lockdep_acquire (sem, subclass,0 , _RET_IP_); +rwsem_acquire (&sem->dep_map, subclass,0 , _RET_IP_); count =atomic_long_add_return (LDSEM_WRITE_BIAS , &sem->count);if ((count &LDSEM_ACTIVE_MASK ) !=LDSEM_ACTIVE_BIAS ) { -lock_stat (sem, contended); +lock_contended (&sem->dep_map, _RET_IP_);if (!down_write_failed (sem, count, timeout)) { -lockdep_release (sem,1 , _RET_IP_); +rwsem_release (&sem->dep_map,1 , _RET_IP_);return 0 ; } } -lock_stat (sem, acquired); +lock_acquired (&sem->dep_map, _RET_IP_);return 1 ; }
@@ -372,8 +349,8 @@ int ldsem_down_read_trylock(struct ld_semaphore *sem)while (count >=0 ) {if (atomic_long_try_cmpxchg (&sem->count, &count, count +LDSEM_READ_BIAS )) { -lockdep_acquire_read (sem,0 ,1 , _RET_IP_); -lock_stat (sem, acquired); +rwsem_acquire_read (&sem->dep_map,0 ,1 , _RET_IP_); +lock_acquired (&sem->dep_map, _RET_IP_);return 1 ; } }
@@ -398,8 +375,8 @@ int ldsem_down_write_trylock(struct ld_semaphore *sem)while ((count &LDSEM_ACTIVE_MASK ) ==0 ) {if (atomic_long_try_cmpxchg (&sem->count, &count, count +LDSEM_WRITE_BIAS )) { -lockdep_acquire (sem,0 ,1 , _RET_IP_); -lock_stat (sem, acquired); +rwsem_acquire (&sem->dep_map,0 ,1 , _RET_IP_); +lock_acquired (&sem->dep_map, _RET_IP_);return 1 ; } }
@@ -413,7 +390,7 @@ void ldsem_up_read(struct ld_semaphore *sem) {long count; -lockdep_release (sem,1 , _RET_IP_); +rwsem_release (&sem->dep_map,1 , _RET_IP_); count =atomic_long_add_return (-LDSEM_READ_BIAS , &sem->count);if (count <0 && (count &LDSEM_ACTIVE_MASK ) ==0 )
@@ -427,7 +404,7 @@ void ldsem_up_write(struct ld_semaphore *sem) {long count; -lockdep_release (sem,1 , _RET_IP_); +rwsem_release (&sem->dep_map,1 , _RET_IP_); count =atomic_long_add_return (-LDSEM_WRITE_BIAS , &sem->count);if (count <0 )
Signature · 1 line
2.13.6
PATCH v5 6/7 tty/ldsem: Add lockdep asserts for ldisc_sem
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.
An exception here is tty_ldisc_open(), as it's called without ldisc_sem locked by tty_init_dev() => tty_ldisc_setup() for the tty->link.
It seem valid as tty_init_dev() will call tty_driver_install_tty() which will find ops->install(). Install will establish tty->link in pty_common_install(), just after allocation of slave tty with alloc_tty_struct(). So, no one should have a reference to slave pty yet.
--- drivers/tty/tty_ldisc.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/tty/tty_ldisc.c +9 -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_exclusive (&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_exclusive (&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_exclusive (&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_exclusive (&tty->ldisc_sem); ld =tty_ldisc_get (tty, disc);if (IS_ERR (ld)) {BUG_ON (disc ==N_TTY );
@@ -760,6 +764,10 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)return retval;if (o_tty) { +/* +* Called without o_tty->ldisc_sem held, as o_tty has been +* just allocated and no one has a reference to it. +*/ retval =tty_ldisc_open (o_tty, o_tty->ldisc);if (retval) {tty_ldisc_close (tty, tty->ldisc);
@@ -825,6 +833,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
Greg Kroah-Hartman
maintainerRe: [PATCHv5 6/7] tty/ldsem: Add lockdep asserts for ldisc_sem
Quoted 4 lines · Dmitry Safonov
Make sure under
CONFIG_LOCKDEPthat each change to line discipline is done with held write semaphor.
Otherwise potential reader will have a good time dereferencing incomplete/uninitialized ldisc.
Note, I don't see patch 5/7 here, did it get lost? Please fix that up for the next resend of this series.
thanks,
greg k-h
Re: [PATCHv5 6/7] tty/ldsem: Add lockdep asserts for ldisc_sem
Quoted 9 lines · Greg Kroah-Hartman
Quoted 4 lines · Dmitry Safonov
Make sure under
CONFIG_LOCKDEPthat each change to line discipline is done with held write semaphor.
Otherwise potential reader will have a good time dereferencing incomplete/uninitialized ldisc.Note, I don't see patch 5/7 here, did it get lost? Please fix that up
for the next resend of this series.
Oh yeah, sorry, I've screwed up the Cc tags for that patch:
https://lore.kernel.org/lkml/[email protected]/
Will resend.
Signature · 2 lines
Thanks,
DmitryPATCH v5 7/7 tty/ldsem: Decrement wait_readers on timeouted down_read()
In reply to Dmitry SafonovIt seems like when ldsem_down_read() fails with timeout, it misses update for sem->wait_readers. By that reason, when writer finally releases write end of the semaphore __ldsem_wake_readers() does adjust sem->count with wrong value:sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS)
I.e, if update comes with 1 missed wait_readers decrement, sem->count will be 0x100000001 which means that there is active reader and it'll make any further writer to fail in acquiring the semaphore.
It looks like, this is a dead-code, because ldsem_down_read() is never called with timeout different than MAX_SCHEDULE_TIMEOUT, so it might be worth to delete timeout parameter and error path fall-back..
--- drivers/tty/tty_ldsem.c | 1 + 1 file changed, 1 insertion(+)
drivers/tty/tty_ldsem.c +1 -0
@@ -212,6 +212,7 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout)raw_spin_lock_irq (&sem->wait_lock);if (waiter.task) {atomic_long_add_return (-LDSEM_WAIT_BIAS , &sem->count); + sem->wait_readers--;list_del (&waiter.list);raw_spin_unlock_irq (&sem->wait_lock);put_task_struct (waiter.task);
Signature · 1 line
2.13.6
Quoted 8 lines · Dmitry Safonov
Hi all,
Three fixes that worth to have in the @stable, as they were hit by different people, including Arista 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.
Quoted 7 lines
The last patch is an optional and probably, timeout can be dropped for
read_lock(). I'll do it if everyone agrees.
(Or as per discussion with Peter in v3, just convert ldisc to a regular rwsem).Thanks,
Dima
I confirm that this patch series fixes the crash for me.
Quoted 66 lines
Changes since v4:
- back to lock ldisc with (5*HZ) timeout intty_reopen()
(LKP report link: lkml.kernel.org/r/<[email protected]>)
- reordered 3/7 with 2/7 for LKP robotChanges since v3:
- Added tested-by Mark Rutland (thanks!)
- Dropped patch withsmp_wmb()- wrong idea
-lockdep_assert_held()should be actuallylockdep_assert_held_exclusive()
- Described whytty_ldisc_open()can be called withoutldisc_semheld for pty slave end (o_tty).
- Added Peter's patch for dropping self-made lockdep annotations
- Fix for a reader(s) of ldisc semaphore waiting for an active reader(s)Changes since v2:
- Added reviewed-by tags
- Hopefully, fixed reported by 0-day issue.
- Added optional fix forwait_readersdecrementChanges since v1:
- Added tested-by/reported-by tags
- Dropped 3/4 (locking tty pair for lockdep sake),
Because of that - not addinglockdep_assert_held()intty_ldisc_open()
- Added 4/4 cleanup to inctty->countonly on success oftty_ldisc_reinit()
- lock ldisc without (5*HZ) timeout intty_reopen()v1 link: lkml.kernel.org/r/<[email protected]> v2 link: lkml.kernel.org/r/<[email protected]> v3 link: lkml.kernel.org/r/<[email protected]> v4 link: lkml.kernel.org/r/<[email protected]>
Cc: Daniel Axtens <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Mark Rutland <[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: Peter Zijlstra <[email protected]>
Cc: "Rong, Chen" <[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 (6):
tty: Droptty->countontty_reopen()failure tty/ldsem: Wake up readers after timed outdown_write()
tty: Holdtty_ldisc_lock()duringtty_reopen()
tty: Simplifytty->countmath intty_reopen()tty/ldsem: Add lockdep asserts forldisc_semtty/ldsem: Decrementwait_readerson timeouteddown_read()Peter Zijlstra (1):
tty/ldsem: Convert to regular lockdep annotationsdrivers/tty/tty_io.c | 13 ++++++++--- drivers/tty/tty_ldisc.c | 9 +++++++ drivers/tty/tty_ldsem.c | 62 ++++++++++++++++++++----------------------------- 3 files changed, 44 insertions(+), 40 deletions(-)Signature · 1 line
2.13.6
Quoted 17 lines · Mikulas Patocka
Quoted 13 lines · Dmitry Safonov
Hi all,
Three fixes that worth to have in the @stable, as they were hit by different people, including Arista 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.
The last patch is an optional and probably, timeout can be dropped for
read_lock(). I'll do it if everyone agrees.
(Or as per discussion with Peter in v3, just convert ldisc to a regular rwsem).
Quoted 6 lines
Quoted 2 lines
Thanks,
DimaI confirm that this patch series fixes the crash for me.
Tested-by Mikulas Patocka <[email protected]>
I was too quick to acknowledge this patchset. It doesn't work.
This patchset fixes the crash, but it introduces another bug - when I type 'reboot' on the console, it prints 'INIT: Switching to runlevel: 6' and then it gets stuck for 80 seconds before proceeding with the reboot. When I revert this patchset 'reboot' reboots the machine without any delay. This bug was reproduced on Debian 5 userspace on pa-risc.
Mikulas
Quoted 32 lines · Mikulas Patocka
Quoted 7 lines · Mikulas Patocka
Quoted 3 lines · Dmitry Safonov
Hi all,
Three fixes that worth to have in the @stable, as they were hit
by
Quoted 3 lines
Quoted 3 lines
different people, including Arista on v4.9 stable.
And for linux-next - adding lockdep asserts for line discipline
changing
Quoted 3 lines
Quoted 3 lines
code, verifying that write ldisc sem will be held forthwith.
The last patch is an optional and probably, timeout can be
dropped for
Quoted 10 lines
Quoted 6 lines
read_lock(). I'll do it if everyone agrees.
(Or as per discussion with Peter in v3, just convert ldisc to a regular rwsem).Thanks,
DimaI confirm that this patch series fixes the crash for me.
Tested-by Mikulas Patocka <[email protected]>I was too quick to acknowledge this patchset. It doesn't work.
Quoted 9 lines
This patchset fixes the crash, but it introduces another bug - when I type
'reboot' on the console, it prints 'INIT: Switching to runlevel: 6' and
then it gets stuck for 80 seconds before proceeding with the reboot. When
I revert this patchset 'reboot' reboots the machine without any delay.
This bug was reproduced on Debian 5 userspace on pa-risc.
Thanks much for the testing, Mikulas.
Could you try to bisect which of the patches causes it? The most important are 1,2,3 - probably, one of them caused it.. I'll stare a bit into the code.
Signature · 2 lines
Thanks,
DmitryQuoted 43 lines · Dmitry Safonov
Quoted 32 lines · Mikulas Patocka
Quoted 7 lines · Mikulas Patocka
Quoted 3 lines · Dmitry Safonov
Hi all,
Three fixes that worth to have in the @stable, as they were hit
by
Quoted 3 lines
Quoted 3 lines
different people, including Arista on v4.9 stable.
And for linux-next - adding lockdep asserts for line discipline
changing
Quoted 3 lines
Quoted 3 lines
code, verifying that write ldisc sem will be held forthwith.
The last patch is an optional and probably, timeout can be
dropped for
Quoted 10 lines
Quoted 6 lines
read_lock(). I'll do it if everyone agrees.
(Or as per discussion with Peter in v3, just convert ldisc to a regular rwsem).Thanks,
DimaI confirm that this patch series fixes the crash for me.
Tested-by Mikulas Patocka <[email protected]>I was too quick to acknowledge this patchset. It doesn't work.
Quoted 9 lines
This patchset fixes the crash, but it introduces another bug - when I type
'reboot' on the console, it prints 'INIT: Switching to runlevel: 6' and
then it gets stuck for 80 seconds before proceeding with the reboot. When
I revert this patchset 'reboot' reboots the machine without any delay.
This bug was reproduced on Debian 5 userspace on pa-risc.
Quoted 4 lines
Thanks much for the testing, Mikulas.
Could you try to bisect which of the patches causes it? The most important are 1,2,3 - probably, one of them caused it.. I'll stare a bit into the code.
The patch 3 causes it.
The hangs during reboot take either 80 seconds, 3 minutes or 3:25.
Mikulas
Quoted 10 lines · Mikulas Patocka
Quoted 4 lines · Dmitry Safonov
Thanks much for the testing, Mikulas.
Could you try to bisect which of the patches causes it? The most important are 1,2,3 - probably, one of them caused it.. I'll stare a bit into the code.The patch 3 causes it.
The hangs during reboot take either 80 seconds, 3 minutes or 3:25.
Thanks much for that again.
Any chance you have sysrq enabled and could print: locks held in system and kernel backtraces for applications?
I suppose, userspace hangs in n_tty_receive_buf_common(), which is better of cause than null-ptr dereference, but I believe we can improve it by stopping a reader earlier if there is any writer pending.
Signature · 2 lines
Thanks,
DmitryQuoted 8 lines · Dmitry Safonov
Quoted 7 lines · Mikulas Patocka
Quoted 4 lines · Dmitry Safonov
Thanks much for the testing, Mikulas.
Could you try to bisect which of the patches causes it? The most important are 1,2,3 - probably, one of them caused it.. I'll stare a bit into the code.
Quoted 7 lines
Quoted 3 lines
The patch 3 causes it.
The hangs during reboot take either 80 seconds, 3 minutes or 3:25.
Thanks much for that again.
Any chance you have sysrq enabled and could print: locks held in system and kernel backtraces for applications?
Stacktrace doesn't work on parisc. It used to work on 4.18, but the kernel 4.19 reportedly requires patched binutils.
Mikulas
Quoted 8 lines
I suppose, userspace hangs in
n_tty_receive_buf_common(), which is better of cause than null-ptr dereference, but I believe we can improve it by stopping a reader earlier if there is any writer pending.Signature · 2 lines
Thanks, Dmitry
Hi,
Quoted 16 lines · Mikulas Patocka
Quoted 12 lines · Dmitry Safonov
Quoted 10 lines · Mikulas Patocka
Quoted 4 lines · Dmitry Safonov
Thanks much for the testing, Mikulas.
Could you try to bisect which of the patches causes it? The most important are 1,2,3 - probably, one of them caused it.. I'll stare a bit into the code.The patch 3 causes it.
The hangs during reboot take either 80 seconds, 3 minutes or 3:25.
Quoted 17 lines
Quoted 3 lines
Thanks much for that again.
Any chance you have sysrq enabled and could print: locks held in system and kernel backtraces for applications?Stacktrace doesn't work on parisc. It used to work on 4.18, but the kernel 4.19 reportedly requires patched binutils.
Mikulas
Quoted 8 lines
I suppose, userspace hangs in
n_tty_receive_buf_common(), which is better of cause than null-ptr dereference, but I believe we can improve it by stopping a reader earlier if there is any writer pending.Signature · 2 lines
Thanks, Dmitry
Any progress here? Or is more info/stacktrace needed about the hang?
Thanks,
-- Pasi