Skip to content

Commit 25a38fa

Browse files
Yi Yanggregkh
Yi Yang
authored andcommitted
tty: tty_jobctrl: fix pid memleak in disassociate_ctty()
[ Upstream commit 11e7f27 ] There is a pid leakage: ------------------------------ unreferenced object 0xffff88810c181940 (size 224): comm "sshd", pid 8191, jiffies 4294946950 (age 524.570s) hex dump (first 32 bytes): 01 00 00 00 00 00 00 00 00 00 00 00 ad 4e ad de .............N.. ff ff ff ff 6b 6b 6b 6b ff ff ff ff ff ff ff ff ....kkkk........ backtrace: [<ffffffff814774e6>] kmem_cache_alloc+0x5c6/0x9b0 [<ffffffff81177342>] alloc_pid+0x72/0x570 [<ffffffff81140ac4>] copy_process+0x1374/0x2470 [<ffffffff81141d77>] kernel_clone+0xb7/0x900 [<ffffffff81142645>] __se_sys_clone+0x85/0xb0 [<ffffffff8114269b>] __x64_sys_clone+0x2b/0x30 [<ffffffff83965a72>] do_syscall_64+0x32/0x80 [<ffffffff83a00085>] entry_SYSCALL_64_after_hwframe+0x61/0xc6 It turns out that there is a race condition between disassociate_ctty() and tty_signal_session_leader(), which caused this leakage. The pid memleak is triggered by the following race: task[sshd] task[bash] ----------------------- ----------------------- disassociate_ctty(); spin_lock_irq(&current->sighand->siglock); put_pid(current->signal->tty_old_pgrp); current->signal->tty_old_pgrp = NULL; tty = tty_kref_get(current->signal->tty); spin_unlock_irq(&current->sighand->siglock); tty_vhangup(); tty_lock(tty); ... tty_signal_session_leader(); spin_lock_irq(&p->sighand->siglock); ... if (tty->ctrl.pgrp) //tty->ctrl.pgrp is not NULL p->signal->tty_old_pgrp = get_pid(tty->ctrl.pgrp); //An extra get spin_unlock_irq(&p->sighand->siglock); ... tty_unlock(tty); if (tty) { tty_lock(tty); ... put_pid(tty->ctrl.pgrp); tty->ctrl.pgrp = NULL; //It's too late ... tty_unlock(tty); } The issue is believed to be introduced by commit c8bcd9c ("tty: Fix ->session locking") who moves the unlock of siglock in disassociate_ctty() above "if (tty)", making a small window allowing tty_signal_session_leader() to kick in. It can be easily reproduced by adding a delay before "if (tty)" and at the entrance of tty_signal_session_leader(). To fix this issue, we move "put_pid(current->signal->tty_old_pgrp)" after "tty->ctrl.pgrp = NULL". Fixes: c8bcd9c ("tty: Fix ->session locking") Signed-off-by: Yi Yang <[email protected]> Co-developed-by: GUO Zihua <[email protected]> Signed-off-by: GUO Zihua <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 10b2a6c commit 25a38fa

File tree

1 file changed

+11
-6
lines changed

1 file changed

+11
-6
lines changed

drivers/tty/tty_jobctrl.c

+11-6
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,7 @@ void disassociate_ctty(int on_exit)
300300
return;
301301
}
302302

303-
spin_lock_irq(&current->sighand->siglock);
304-
put_pid(current->signal->tty_old_pgrp);
305-
current->signal->tty_old_pgrp = NULL;
306-
tty = tty_kref_get(current->signal->tty);
307-
spin_unlock_irq(&current->sighand->siglock);
308-
303+
tty = get_current_tty();
309304
if (tty) {
310305
unsigned long flags;
311306

@@ -320,6 +315,16 @@ void disassociate_ctty(int on_exit)
320315
tty_kref_put(tty);
321316
}
322317

318+
/* If tty->ctrl.pgrp is not NULL, it may be assigned to
319+
* current->signal->tty_old_pgrp in a race condition, and
320+
* cause pid memleak. Release current->signal->tty_old_pgrp
321+
* after tty->ctrl.pgrp set to NULL.
322+
*/
323+
spin_lock_irq(&current->sighand->siglock);
324+
put_pid(current->signal->tty_old_pgrp);
325+
current->signal->tty_old_pgrp = NULL;
326+
spin_unlock_irq(&current->sighand->siglock);
327+
323328
/* Now clear signal->tty under the lock */
324329
read_lock(&tasklist_lock);
325330
session_clear_tty(task_session(current));

0 commit comments

Comments
 (0)