Re: [PATCH v3] tty: ldisc: fix deadlock between ldisc_sem and rtnl_mutex

"Zhou, Yun" <[email protected]> Sun, 26 Jul 2026 11:25:41 +0800
Newsgroups org.kernel.vger.linux-can,dev.linux.lists.sashiko-reviews
Message-ID <[email protected]>
On 7/25/2026 4:47 PM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> 
> New issues:
> - [High] The patch replaces a lockdep splat with a silent waitqueue deadlock on the success path.
> 
> Pre-existing issues:
> - [High] Incomplete fix for circular locking between ldisc_sem and rtnl_mutex, leaving deadlocks on teardown, error, and hangup paths.
> --
> 
> --- Patch [1]: [PATCH v3] tty: ldisc: fix deadlock between ldisc_sem and rtnl_mutex ---
> commit 98cde28b47f83f4a2bf71bb1e35f53d9a7a36294
> Author: Yun Zhou <[email protected]>
> 
> tty: ldisc: fix deadlock between ldisc_sem and rtnl_mutex
> 
> This commit attempts to fix a circular lock dependency between ldisc_sem
> and rtnl_mutex reported by syzbot. It moves the tty line discipline .open()
> callback outside the ldisc_sem write lock in tty_set_ldisc(). It also adds
> logic in tty_ldisc_ref_wait() to wait for the ldisc transition to complete.
> 
> Closes: https://syzkaller.appspot.com/bug?extid=de610eeef174bd59a8a3
> 
>> diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
>> index 27fe8236f6623..6ec93e6b84988 100644
>> --- a/drivers/tty/tty_ldisc.c
>> +++ b/drivers/tty/tty_ldisc.c
>> @@ -242,6 +242,16 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
>>
>>        ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
>>        ld = tty->ldisc;
>> +     while (!ld && !test_bit(TTY_HUPPED, &tty->flags)) {
>> +             ldsem_up_read(&tty->ldisc_sem);
>> +
>> +             /* ldisc may be NULL during a discipline switch; wait and retry */
>> +             wait_event(tty->read_wait,
>> +                        READ_ONCE(tty->ldisc) != NULL ||
>> +                        test_bit(TTY_HUPPED, &tty->flags));
> 
> [Severity: High]
> Does this wait_event() create a silent waitqueue deadlock that replaces the
> lockdep splat?
> 
> If thread A switches to a network ldisc like slcan, it will set tty->ldisc
> to NULL in tty_set_ldisc() and drop ldisc_sem, but then block waiting for
> rtnl_mutex in the .open() callback.
> 
> Concurrently, if thread B already holds rtnl_mutex and calls tty_poll(),
> it enters tty_ldisc_ref_wait() here. Thread B will observe tty->ldisc as NULL
> and block uninterruptibly in this wait_event(), waiting for thread A to
> re-assign tty->ldisc.
> 

No, this scenario does not exist. tty_poll() is only called from 
userspace poll/epoll/select paths, which never hold rtnl_mutex. And All 
callers of tty_ldisc_ref_wait() are userspace entry points: 
tty_read/tty_write/tty_poll/tty_ioctl/tty_open/tty_show_fdinfo/paste_selection/tty_perform_flush. 
None of these are reachable from any code path that holds rtnl_mutex.

> Since thread A is blocked waiting for thread B to release rtnl_mutex,
> will both threads hang forever without a lockdep warning?
>