Commit 05473283 authored by Martin Schwidefsky's avatar Martin Schwidefsky

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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent aeaf7002
...@@ -334,37 +334,41 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe, ...@@ -334,37 +334,41 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
int cmd, int perm) int cmd, int perm)
{ {
struct kbentry tmp; struct kbentry tmp;
unsigned long kb_index, kb_table;
ushort *key_map, val, ov; ushort *key_map, val, ov;
if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
return -EFAULT; return -EFAULT;
kb_index = (unsigned long) tmp.kb_index;
#if NR_KEYS < 256 #if NR_KEYS < 256
if (tmp.kb_index >= NR_KEYS) if (kb_index >= NR_KEYS)
return -EINVAL; return -EINVAL;
#endif #endif
kb_table = (unsigned long) tmp.kb_table;
#if MAX_NR_KEYMAPS < 256 #if MAX_NR_KEYMAPS < 256
if (tmp.kb_table >= MAX_NR_KEYMAPS) if (kb_table >= MAX_NR_KEYMAPS)
return -EINVAL; return -EINVAL;
kb_table = array_index_nospec(kb_table , MAX_NR_KEYMAPS);
#endif #endif
switch (cmd) { switch (cmd) {
case KDGKBENT: case KDGKBENT:
key_map = kbd->key_maps[tmp.kb_table]; key_map = kbd->key_maps[kb_table];
if (key_map) { if (key_map) {
val = U(key_map[tmp.kb_index]); val = U(key_map[kb_index]);
if (KTYP(val) >= KBD_NR_TYPES) if (KTYP(val) >= KBD_NR_TYPES)
val = K_HOLE; val = K_HOLE;
} else } else
val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP); val = (kb_index ? K_HOLE : K_NOSUCHMAP);
return put_user(val, &user_kbe->kb_value); return put_user(val, &user_kbe->kb_value);
case KDSKBENT: case KDSKBENT:
if (!perm) if (!perm)
return -EPERM; return -EPERM;
if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) { if (!kb_index && tmp.kb_value == K_NOSUCHMAP) {
/* disallocate map */ /* disallocate map */
key_map = kbd->key_maps[tmp.kb_table]; key_map = kbd->key_maps[kb_table];
if (key_map) { if (key_map) {
kbd->key_maps[tmp.kb_table] = NULL; kbd->key_maps[kb_table] = NULL;
kfree(key_map); kfree(key_map);
} }
break; break;
...@@ -375,18 +379,18 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe, ...@@ -375,18 +379,18 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)]) if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)])
return -EINVAL; return -EINVAL;
if (!(key_map = kbd->key_maps[tmp.kb_table])) { if (!(key_map = kbd->key_maps[kb_table])) {
int j; int j;
key_map = kmalloc(sizeof(plain_map), key_map = kmalloc(sizeof(plain_map),
GFP_KERNEL); GFP_KERNEL);
if (!key_map) if (!key_map)
return -ENOMEM; return -ENOMEM;
kbd->key_maps[tmp.kb_table] = key_map; kbd->key_maps[kb_table] = key_map;
for (j = 0; j < NR_KEYS; j++) for (j = 0; j < NR_KEYS; j++)
key_map[j] = U(K_HOLE); key_map[j] = U(K_HOLE);
} }
ov = U(key_map[tmp.kb_index]); ov = U(key_map[kb_index]);
if (tmp.kb_value == ov) if (tmp.kb_value == ov)
break; /* nothing to do */ break; /* nothing to do */
/* /*
...@@ -395,7 +399,7 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe, ...@@ -395,7 +399,7 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) && if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) &&
!capable(CAP_SYS_ADMIN)) !capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
key_map[tmp.kb_index] = U(tmp.kb_value); key_map[kb_index] = U(tmp.kb_value);
break; break;
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment