Skip to content

Commit 033b15f

Browse files
Sergei Trofimovichgregkh
Sergei Trofimovich
authored andcommitted
tty/vt: fix write/write race in ioctl(KDSKBSENT) handler
commit 46ca3f7 upstream. The bug manifests as an attempt to access deallocated memory: BUG: unable to handle kernel paging request at ffff9c8735448000 #PF error: [PROT] [WRITE] PGD 288a05067 P4D 288a05067 PUD 288a07067 PMD 7f60c2063 PTE 80000007f5448161 Oops: 0003 [#1] PREEMPT SMP CPU: 6 PID: 388 Comm: loadkeys Tainted: G C 5.0.0-rc6-00153-g5ded5871030e #91 Hardware name: Gigabyte Technology Co., Ltd. To be filled by O.E.M./H77M-D3H, BIOS F12 11/14/2013 RIP: 0010:__memmove+0x81/0x1a0 Code: 4c 89 4f 10 4c 89 47 18 48 8d 7f 20 73 d4 48 83 c2 20 e9 a2 00 00 00 66 90 48 89 d1 4c 8b 5c 16 f8 4c 8d 54 17 f8 48 c1 e9 03 <f3> 48 a5 4d 89 1a e9 0c 01 00 00 0f 1f 40 00 48 89 d1 4c 8b 1e 49 RSP: 0018:ffffa1b9002d7d08 EFLAGS: 00010203 RAX: ffff9c873541af43 RBX: ffff9c873541af43 RCX: 00000c6f105cd6bf RDX: 0000637882e986b6 RSI: ffff9c8735447ffb RDI: ffff9c8735447ffb RBP: ffff9c8739cd3800 R08: ffff9c873b802f00 R09: 00000000fffff73b R10: ffffffffb82b35f1 R11: 00505b1b004d5b1b R12: 0000000000000000 R13: ffff9c873541af3d R14: 000000000000000b R15: 000000000000000c FS: 00007f450c390580(0000) GS:ffff9c873f180000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffff9c8735448000 CR3: 00000007e213c002 CR4: 00000000000606e0 Call Trace: vt_do_kdgkb_ioctl+0x34d/0x440 vt_ioctl+0xba3/0x1190 ? __bpf_prog_run32+0x39/0x60 ? mem_cgroup_commit_charge+0x7b/0x4e0 tty_ioctl+0x23f/0x920 ? preempt_count_sub+0x98/0xe0 ? __seccomp_filter+0x67/0x600 do_vfs_ioctl+0xa2/0x6a0 ? syscall_trace_enter+0x192/0x2d0 ksys_ioctl+0x3a/0x70 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x54/0xe0 entry_SYSCALL_64_after_hwframe+0x49/0xbe The bug manifests on systemd systems with multiple vtcon devices: # cat /sys/devices/virtual/vtconsole/vtcon0/name (S) dummy device # cat /sys/devices/virtual/vtconsole/vtcon1/name (M) frame buffer device There systemd runs 'loadkeys' tool in tapallel for each vtcon instance. This causes two parallel ioctl(KDSKBSENT) calls to race into adding the same entry into 'func_table' array at: drivers/tty/vt/keyboard.c:vt_do_kdgkb_ioctl() The function has no locking around writes to 'func_table'. The simplest reproducer is to have initrams with the following init on a 8-CPU machine x86_64: #!/bin/sh loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & wait The change adds lock on write path only. Reads are still racy. CC: Greg Kroah-Hartman <[email protected]> CC: Jiri Slaby <[email protected]> Link: https://lkml.org/lkml/2019/2/17/256 Signed-off-by: Sergei Trofimovich <[email protected]> Cc: stable <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 770e812 commit 033b15f

File tree

1 file changed

+27
-6
lines changed

1 file changed

+27
-6
lines changed

drivers/tty/vt/keyboard.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
123123
static struct input_handler kbd_handler;
124124
static DEFINE_SPINLOCK(kbd_event_lock);
125125
static DEFINE_SPINLOCK(led_lock);
126+
static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */
126127
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
127128
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
128129
static bool dead_key_next;
@@ -1990,11 +1991,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
19901991
char *p;
19911992
u_char *q;
19921993
u_char __user *up;
1993-
int sz;
1994+
int sz, fnw_sz;
19941995
int delta;
19951996
char *first_free, *fj, *fnw;
19961997
int i, j, k;
19971998
int ret;
1999+
unsigned long flags;
19982000

19992001
if (!capable(CAP_SYS_TTY_CONFIG))
20002002
perm = 0;
@@ -2037,18 +2039,27 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
20372039
goto reterr;
20382040
}
20392041

2042+
fnw = NULL;
2043+
fnw_sz = 0;
2044+
/* race aginst other writers */
2045+
again:
2046+
spin_lock_irqsave(&func_buf_lock, flags);
20402047
q = func_table[i];
2048+
2049+
/* fj pointer to next entry after 'q' */
20412050
first_free = funcbufptr + (funcbufsize - funcbufleft);
20422051
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
20432052
;
20442053
if (j < MAX_NR_FUNC)
20452054
fj = func_table[j];
20462055
else
20472056
fj = first_free;
2048-
2057+
/* buffer usage increase by new entry */
20492058
delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
2059+
20502060
if (delta <= funcbufleft) { /* it fits in current buf */
20512061
if (j < MAX_NR_FUNC) {
2062+
/* make enough space for new entry at 'fj' */
20522063
memmove(fj + delta, fj, first_free - fj);
20532064
for (k = j; k < MAX_NR_FUNC; k++)
20542065
if (func_table[k])
@@ -2061,20 +2072,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
20612072
sz = 256;
20622073
while (sz < funcbufsize - funcbufleft + delta)
20632074
sz <<= 1;
2064-
fnw = kmalloc(sz, GFP_KERNEL);
2065-
if(!fnw) {
2066-
ret = -ENOMEM;
2067-
goto reterr;
2075+
if (fnw_sz != sz) {
2076+
spin_unlock_irqrestore(&func_buf_lock, flags);
2077+
kfree(fnw);
2078+
fnw = kmalloc(sz, GFP_KERNEL);
2079+
fnw_sz = sz;
2080+
if (!fnw) {
2081+
ret = -ENOMEM;
2082+
goto reterr;
2083+
}
2084+
goto again;
20682085
}
20692086

20702087
if (!q)
20712088
func_table[i] = fj;
2089+
/* copy data before insertion point to new location */
20722090
if (fj > funcbufptr)
20732091
memmove(fnw, funcbufptr, fj - funcbufptr);
20742092
for (k = 0; k < j; k++)
20752093
if (func_table[k])
20762094
func_table[k] = fnw + (func_table[k] - funcbufptr);
20772095

2096+
/* copy data after insertion point to new location */
20782097
if (first_free > fj) {
20792098
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
20802099
for (k = j; k < MAX_NR_FUNC; k++)
@@ -2087,7 +2106,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
20872106
funcbufleft = funcbufleft - delta + sz - funcbufsize;
20882107
funcbufsize = sz;
20892108
}
2109+
/* finally insert item itself */
20902110
strcpy(func_table[i], kbs->kb_string);
2111+
spin_unlock_irqrestore(&func_buf_lock, flags);
20912112
break;
20922113
}
20932114
ret = 0;

0 commit comments

Comments
 (0)