Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/keyboard: sanitize array index in do_kdsk_ioctl

The kbd_ioctl uses two user controlled indexes for KDGKBENT/KDSKBENT.
Use array_index_nospec to prevent any out of bounds speculation.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+16 -12
+16 -12
drivers/s390/char/keyboard.c
··· 334 334 int cmd, int perm) 335 335 { 336 336 struct kbentry tmp; 337 + unsigned long kb_index, kb_table; 337 338 ushort *key_map, val, ov; 338 339 339 340 if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) 340 341 return -EFAULT; 342 + kb_index = (unsigned long) tmp.kb_index; 341 343 #if NR_KEYS < 256 342 - if (tmp.kb_index >= NR_KEYS) 344 + if (kb_index >= NR_KEYS) 343 345 return -EINVAL; 344 346 #endif 347 + kb_table = (unsigned long) tmp.kb_table; 345 348 #if MAX_NR_KEYMAPS < 256 346 - if (tmp.kb_table >= MAX_NR_KEYMAPS) 349 + if (kb_table >= MAX_NR_KEYMAPS) 347 350 return -EINVAL; 351 + kb_table = array_index_nospec(kb_table , MAX_NR_KEYMAPS); 348 352 #endif 349 353 350 354 switch (cmd) { 351 355 case KDGKBENT: 352 - key_map = kbd->key_maps[tmp.kb_table]; 356 + key_map = kbd->key_maps[kb_table]; 353 357 if (key_map) { 354 - val = U(key_map[tmp.kb_index]); 358 + val = U(key_map[kb_index]); 355 359 if (KTYP(val) >= KBD_NR_TYPES) 356 360 val = K_HOLE; 357 361 } else 358 - val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP); 362 + val = (kb_index ? K_HOLE : K_NOSUCHMAP); 359 363 return put_user(val, &user_kbe->kb_value); 360 364 case KDSKBENT: 361 365 if (!perm) 362 366 return -EPERM; 363 - if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) { 367 + if (!kb_index && tmp.kb_value == K_NOSUCHMAP) { 364 368 /* disallocate map */ 365 - key_map = kbd->key_maps[tmp.kb_table]; 369 + key_map = kbd->key_maps[kb_table]; 366 370 if (key_map) { 367 - kbd->key_maps[tmp.kb_table] = NULL; 371 + kbd->key_maps[kb_table] = NULL; 368 372 kfree(key_map); 369 373 } 370 374 break; ··· 379 375 if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)]) 380 376 return -EINVAL; 381 377 382 - if (!(key_map = kbd->key_maps[tmp.kb_table])) { 378 + if (!(key_map = kbd->key_maps[kb_table])) { 383 379 int j; 384 380 385 381 key_map = kmalloc(sizeof(plain_map), 386 382 GFP_KERNEL); 387 383 if (!key_map) 388 384 return -ENOMEM; 389 - kbd->key_maps[tmp.kb_table] = key_map; 385 + kbd->key_maps[kb_table] = key_map; 390 386 for (j = 0; j < NR_KEYS; j++) 391 387 key_map[j] = U(K_HOLE); 392 388 } 393 - ov = U(key_map[tmp.kb_index]); 389 + ov = U(key_map[kb_index]); 394 390 if (tmp.kb_value == ov) 395 391 break; /* nothing to do */ 396 392 /* ··· 399 395 if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) && 400 396 !capable(CAP_SYS_ADMIN)) 401 397 return -EPERM; 402 - key_map[tmp.kb_index] = U(tmp.kb_value); 398 + key_map[kb_index] = U(tmp.kb_value); 403 399 break; 404 400 } 405 401 return 0;