diff options
author | Andy Lutomirski <luto@kernel.org> | 2016-02-15 11:32:33 -0500 |
---|---|---|
committer | Darren Hart <dvhart@linux.intel.com> | 2016-03-23 13:05:44 -0400 |
commit | 18b6f80f5095036bd2204c54c89a5a0f5a785e3d (patch) | |
tree | de5e85f413d4c0a5eadf0dd9e2251abbd74c2b01 | |
parent | 0c41a08e131d753af48ae6f052aed8d530b9c976 (diff) |
dell-wmi: Stop storing pointers to DMI tables
The dmi_walk function maps the DMI table, walks it, and unmaps it.
This means that the dell_bios_hotkey_table that find_hk_type stores
points to unmapped memory by the time it gets read.
I've been able to trigger crashes caused by the stale pointer a
couple of times, but never on a stock kernel.
Fix it by generating the keymap in the dmi_walk callback instead of
storing a pointer.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Acked-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 74 |
1 files changed, 46 insertions, 28 deletions
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 368e193c2741..d6ae69e0a787 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -120,7 +120,10 @@ struct dell_bios_hotkey_table { | |||
120 | 120 | ||
121 | }; | 121 | }; |
122 | 122 | ||
123 | static const struct dell_bios_hotkey_table *dell_bios_hotkey_table; | 123 | struct dell_dmi_results { |
124 | int err; | ||
125 | struct key_entry *keymap; | ||
126 | }; | ||
124 | 127 | ||
125 | /* Uninitialized entries here are KEY_RESERVED == 0. */ | 128 | /* Uninitialized entries here are KEY_RESERVED == 0. */ |
126 | static const u16 bios_to_linux_keycode[256] __initconst = { | 129 | static const u16 bios_to_linux_keycode[256] __initconst = { |
@@ -337,20 +340,34 @@ static void dell_wmi_notify(u32 value, void *context) | |||
337 | kfree(obj); | 340 | kfree(obj); |
338 | } | 341 | } |
339 | 342 | ||
340 | static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) | 343 | static void __init handle_dmi_entry(const struct dmi_header *dm, |
344 | void *opaque) | ||
341 | { | 345 | { |
342 | int hotkey_num = (dell_bios_hotkey_table->header.length - 4) / | 346 | struct dell_dmi_results *results = opaque; |
343 | sizeof(struct dell_bios_keymap_entry); | 347 | struct dell_bios_hotkey_table *table; |
344 | struct key_entry *keymap; | 348 | struct key_entry *keymap; |
345 | int i; | 349 | int hotkey_num, i; |
350 | |||
351 | if (results->err || results->keymap) | ||
352 | return; /* We already found the hotkey table. */ | ||
353 | |||
354 | if (dm->type != 0xb2 || dm->length <= 6) | ||
355 | return; | ||
356 | |||
357 | table = container_of(dm, struct dell_bios_hotkey_table, header); | ||
358 | |||
359 | hotkey_num = (table->header.length - 4) / | ||
360 | sizeof(struct dell_bios_keymap_entry); | ||
346 | 361 | ||
347 | keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL); | 362 | keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL); |
348 | if (!keymap) | 363 | if (!keymap) { |
349 | return NULL; | 364 | results->err = -ENOMEM; |
365 | return; | ||
366 | } | ||
350 | 367 | ||
351 | for (i = 0; i < hotkey_num; i++) { | 368 | for (i = 0; i < hotkey_num; i++) { |
352 | const struct dell_bios_keymap_entry *bios_entry = | 369 | const struct dell_bios_keymap_entry *bios_entry = |
353 | &dell_bios_hotkey_table->keymap[i]; | 370 | &table->keymap[i]; |
354 | 371 | ||
355 | /* Uninitialized entries are 0 aka KEY_RESERVED. */ | 372 | /* Uninitialized entries are 0 aka KEY_RESERVED. */ |
356 | u16 keycode = (bios_entry->keycode < | 373 | u16 keycode = (bios_entry->keycode < |
@@ -379,11 +396,12 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) | |||
379 | 396 | ||
380 | keymap[hotkey_num].type = KE_END; | 397 | keymap[hotkey_num].type = KE_END; |
381 | 398 | ||
382 | return keymap; | 399 | results->keymap = keymap; |
383 | } | 400 | } |
384 | 401 | ||
385 | static int __init dell_wmi_input_setup(void) | 402 | static int __init dell_wmi_input_setup(void) |
386 | { | 403 | { |
404 | struct dell_dmi_results dmi_results = {}; | ||
387 | int err; | 405 | int err; |
388 | 406 | ||
389 | dell_wmi_input_dev = input_allocate_device(); | 407 | dell_wmi_input_dev = input_allocate_device(); |
@@ -394,20 +412,31 @@ static int __init dell_wmi_input_setup(void) | |||
394 | dell_wmi_input_dev->phys = "wmi/input0"; | 412 | dell_wmi_input_dev->phys = "wmi/input0"; |
395 | dell_wmi_input_dev->id.bustype = BUS_HOST; | 413 | dell_wmi_input_dev->id.bustype = BUS_HOST; |
396 | 414 | ||
397 | if (dell_new_hk_type) { | 415 | if (dmi_walk(handle_dmi_entry, &dmi_results)) { |
398 | const struct key_entry *keymap = dell_wmi_prepare_new_keymap(); | 416 | /* |
399 | if (!keymap) { | 417 | * Historically, dell-wmi ignored dmi_walk errors. A failure |
400 | err = -ENOMEM; | 418 | * is certainly surprising, but it probably just indicates |
401 | goto err_free_dev; | 419 | * a very old laptop. |
402 | } | 420 | */ |
421 | pr_warn("no DMI; using the old-style hotkey interface\n"); | ||
422 | } | ||
423 | |||
424 | if (dmi_results.err) { | ||
425 | err = dmi_results.err; | ||
426 | goto err_free_dev; | ||
427 | } | ||
428 | |||
429 | if (dmi_results.keymap) { | ||
430 | dell_new_hk_type = true; | ||
403 | 431 | ||
404 | err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); | 432 | err = sparse_keymap_setup(dell_wmi_input_dev, |
433 | dmi_results.keymap, NULL); | ||
405 | 434 | ||
406 | /* | 435 | /* |
407 | * Sparse keymap library makes a copy of keymap so we | 436 | * Sparse keymap library makes a copy of keymap so we |
408 | * don't need the original one that was allocated. | 437 | * don't need the original one that was allocated. |
409 | */ | 438 | */ |
410 | kfree(keymap); | 439 | kfree(dmi_results.keymap); |
411 | } else { | 440 | } else { |
412 | err = sparse_keymap_setup(dell_wmi_input_dev, | 441 | err = sparse_keymap_setup(dell_wmi_input_dev, |
413 | dell_wmi_legacy_keymap, NULL); | 442 | dell_wmi_legacy_keymap, NULL); |
@@ -434,15 +463,6 @@ static void dell_wmi_input_destroy(void) | |||
434 | input_unregister_device(dell_wmi_input_dev); | 463 | input_unregister_device(dell_wmi_input_dev); |
435 | } | 464 | } |
436 | 465 | ||
437 | static void __init find_hk_type(const struct dmi_header *dm, void *dummy) | ||
438 | { | ||
439 | if (dm->type == 0xb2 && dm->length > 6) { | ||
440 | dell_new_hk_type = true; | ||
441 | dell_bios_hotkey_table = | ||
442 | container_of(dm, struct dell_bios_hotkey_table, header); | ||
443 | } | ||
444 | } | ||
445 | |||
446 | /* | 466 | /* |
447 | * Descriptor buffer is 128 byte long and contains: | 467 | * Descriptor buffer is 128 byte long and contains: |
448 | * | 468 | * |
@@ -524,8 +544,6 @@ static int __init dell_wmi_init(void) | |||
524 | if (err) | 544 | if (err) |
525 | return err; | 545 | return err; |
526 | 546 | ||
527 | dmi_walk(find_hk_type, NULL); | ||
528 | |||
529 | err = dell_wmi_input_setup(); | 547 | err = dell_wmi_input_setup(); |
530 | if (err) | 548 | if (err) |
531 | return err; | 549 | return err; |