diff mbox series

tty: fix data races in read buffer management subroutines

Message ID 20250623102544.510761-1-dmantipov@yandex.ru
State New
Headers show
Series tty: fix data races in read buffer management subroutines | expand

Commit Message

Dmitry Antipov June 23, 2025, 10:25 a.m. UTC
When running anything causes an intense enough tty output, for
example 'find /' on a tty console, KCSAN is likely to report a
few data races around an internal routines which examines and
manages the read buffer:

BUG: KCSAN: data-race in n_tty_read / n_tty_receive_buf_common

write (marked) to 0xffffc90003ae5008 of 8 bytes by task 100 on cpu 1:
 n_tty_receive_buf_common+0x710/0xc30
 n_tty_receive_buf2+0x3d/0x60
 tty_ldisc_receive_buf+0x6b/0x100
 tty_port_default_receive_buf+0x63/0xa0
 flush_to_ldisc+0x169/0x3c0
 process_scheduled_works+0x6fe/0xf40
 worker_thread+0x53b/0x7b0
 kthread+0x4f8/0x590
 ret_from_fork+0x28c/0x450
 ret_from_fork_asm+0x1a/0x30

read to 0xffffc90003ae5008 of 8 bytes by task 5844 on cpu 4:
 n_tty_read+0x6b2/0xe00
 tty_read+0x16f/0x490
 vfs_read+0x595/0x600
 ksys_read+0xe7/0x1b0
 __x64_sys_read+0x4a/0x60
 x64_sys_call+0x2ff1/0x32b0
 do_syscall_64+0xfa/0x3b0
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

BUG: KCSAN: data-race in n_tty_poll / n_tty_receive_buf_common

write (marked) to 0xffffc90003ae5008 of 8 bytes by task 68 on cpu 6:
 n_tty_receive_buf_common+0x710/0xc30
 n_tty_receive_buf2+0x3d/0x60
 tty_ldisc_receive_buf+0x6b/0x100
 tty_port_default_receive_buf+0x63/0xa0
 flush_to_ldisc+0x169/0x3c0
 process_scheduled_works+0x6fe/0xf40
 worker_thread+0x53b/0x7b0
 kthread+0x4f8/0x590
 ret_from_fork+0x28c/0x450
 ret_from_fork_asm+0x1a/0x30

read to 0xffffc90003ae5008 of 8 bytes by task 5844 on cpu 3:
 n_tty_poll+0x2fb/0x480
 tty_poll+0x80/0x100
 do_sys_poll+0x618/0xb30
 __se_sys_ppoll+0x1c3/0x210
 __x64_sys_ppoll+0x71/0x90
 x64_sys_call+0x3079/0x32b0
 do_syscall_64+0xfa/0x3b0
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

BUG: KCSAN: data-race in n_tty_check_unthrottle / n_tty_receive_buf_common

write (marked) to 0xffffc90003ae5008 of 8 bytes by task 68 on cpu 4:
 n_tty_receive_buf_common+0x710/0xc30
 n_tty_receive_buf2+0x3d/0x60
 tty_ldisc_receive_buf+0x6b/0x100
 tty_port_default_receive_buf+0x63/0xa0
 flush_to_ldisc+0x169/0x3c0
 process_scheduled_works+0x6fe/0xf40
 worker_thread+0x53b/0x7b0
 kthread+0x4f8/0x590
 ret_from_fork+0x28c/0x450
 ret_from_fork_asm+0x1a/0x30

read to 0xffffc90003ae5008 of 8 bytes by task 5844 on cpu 5:
 n_tty_check_unthrottle+0x86/0x1d0
 n_tty_read+0xb05/0xe00
 tty_read+0x16f/0x490
 vfs_read+0x595/0x600
 ksys_read+0xe7/0x1b0
 __x64_sys_read+0x4a/0x60
 x64_sys_call+0x2ff1/0x32b0
 do_syscall_64+0xfa/0x3b0
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Fix all of them by using smp_load_acquire() accessing buffer's
head and tail marks in chars_in_buffer() and input_available_p().

Fixes: 70aca71f92ca ("n_tty: Fix unordered accesses to lockless read buffer")
Signed-off-by: Dmitry Antipov <dmantipov@yandex.ru>
---
 drivers/tty/n_tty.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 6af3f3a0b531..9478d2bd637a 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -213,9 +213,11 @@  static void n_tty_kick_worker(const struct tty_struct *tty)
 static ssize_t chars_in_buffer(const struct tty_struct *tty)
 {
 	const struct n_tty_data *ldata = tty->disc_data;
-	size_t head = ldata->icanon ? ldata->canon_head : ldata->commit_head;
+	size_t head = ldata->icanon ?
+		      smp_load_acquire(&ldata->canon_head) :
+		      smp_load_acquire(&ldata->commit_head);
 
-	return head - ldata->read_tail;
+	return head - smp_load_acquire(&ldata->read_tail);
 }
 
 /**
@@ -1919,11 +1921,12 @@  static inline int input_available_p(const struct tty_struct *tty, int poll)
 {
 	const struct n_tty_data *ldata = tty->disc_data;
 	int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1;
+	size_t tail = smp_load_acquire(&ldata->read_tail);
 
 	if (ldata->icanon && !L_EXTPROC(tty))
-		return ldata->canon_head != ldata->read_tail;
+		return smp_load_acquire(&ldata->canon_head) != tail;
 	else
-		return ldata->commit_head - ldata->read_tail >= amt;
+		return smp_load_acquire(&ldata->commit_head) - tail >= amt;
 }
 
 /**