aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2016-02-15 11:32:33 -0500
committerDarren Hart <dvhart@linux.intel.com>2016-03-23 13:05:44 -0400
commit18b6f80f5095036bd2204c54c89a5a0f5a785e3d (patch)
treede5e85f413d4c0a5eadf0dd9e2251abbd74c2b01
parent0c41a08e131d753af48ae6f052aed8d530b9c976 (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.c74
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
123static const struct dell_bios_hotkey_table *dell_bios_hotkey_table; 123struct 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. */
126static const u16 bios_to_linux_keycode[256] __initconst = { 129static 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
340static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) 343static 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
385static int __init dell_wmi_input_setup(void) 402static 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
437static 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;