dell-wmi: DMI misuse fixes
v3 cover dell-wmi: DMI misuse fixes
This may all be 4.5 material.
Changes from v2:
- Dropped the dmi_walk return value change. I'll resend it separately.
- Rewrite the hotkey_num change
- Add the select DMI patch.
- Fix patch 1 error handling.
Changes from v1:
- Patch 1 has minor tweaks from Jean.
- Patches 2 and 3 are new.
Andy Lutomirski (3):
dell-wmi: Stop storing pointers to DMI tables
dell-wmi, dell-laptop: select DMI
dell-wmi: Clean up hotkey table size check
drivers/platform/x86/Kconfig | 2 + drivers/platform/x86/dell-wmi.c | 85 +++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 28 deletions(-)
Signature · 1 line
2.5.0
PATCH v3 1/3 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.
---
Changes from v2:
- dmi_walk failure is no longer fatal (Jean)
Changes from v1:
- Rename handle_dmi_table to handle_dmi_entry (Jean)
- Remove useless assignment to results->err (Jean)
drivers/platform/x86/dell-wmi.c | 74 +++++++++++++++++++++++++----------------
1 file changed, 46 insertions(+), 28 deletions(-)
drivers/platform/x86/dell-wmi.c +46 -28
@@ -114,7 +114,10 @@ struct dell_bios_hotkey_table { }; -static const struct dell_bios_hotkey_table *dell_bios_hotkey_table; +struct dell_dmi_results { +int err; +struct key_entry *keymap; +};/* Uninitialized entries here are KEY_RESERVED == 0. */ static const u16 bios_to_linux_keycode[256 ]__initconst = {
@@ -315,20 +318,34 @@ static void dell_wmi_notify(u32 value, void *context)kfree (obj); } -static const struct key_entry *__init dell_wmi_prepare_new_keymap (void ) +static void __init handle_dmi_entry (const struct dmi_header *dm, +void *opaque) { -int hotkey_num = (dell_bios_hotkey_table->header.length -4 ) / -sizeof (struct dell_bios_keymap_entry ); +struct dell_dmi_results *results = opaque; +struct dell_bios_hotkey_table *table;struct key_entry *keymap; -int i; +int hotkey_num, i; + +if (results->err || results->keymap) +return ;/* We already found the hotkey table. */ + +if (dm->type !=0xb2 || dm->length <=6 ) +return ; + + table = container_of(dm, struct dell_bios_hotkey_table, header); + + hotkey_num = (table->header.length -4 ) / +sizeof (struct dell_bios_keymap_entry ); keymap =kcalloc (hotkey_num +1 ,sizeof (struct key_entry ),GFP_KERNEL ); -if (!keymap) -return NULL ; +if (!keymap) { + results->err = -ENOMEM ; +return ; + }for (i =0 ; i < hotkey_num; i++) {const struct dell_bios_keymap_entry *bios_entry = - &dell_bios_hotkey_table->keymap[i]; + &table->keymap[i];/* Uninitialized entries are 0 aka KEY_RESERVED. */ u16 keycode = (bios_entry->keycode <
@@ -357,11 +374,12 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) keymap[hotkey_num].type =KE_END ; -return keymap; + results->keymap = keymap; }static int __init dell_wmi_input_setup (void ) { +struct dell_dmi_results dmi_results = {};int err; dell_wmi_input_dev =input_allocate_device ();
@@ -372,20 +390,31 @@ static int __init dell_wmi_input_setup(void) dell_wmi_input_dev->phys ="wmi/input0" ; dell_wmi_input_dev->id.bustype =BUS_HOST ; -if (dell_new_hk_type) { -const struct key_entry *keymap =dell_wmi_prepare_new_keymap (); -if (!keymap) { - err = -ENOMEM ; - goto err_free_dev; - } +if (dmi_walk (handle_dmi_entry, &dmi_results)) { +/* +* Historically, dell-wmi ignored dmi_walk errors. A failure +* is certainly surprising, but it probably just indicates +* a very old laptop. +*/ +pr_warn ("no DMI; using the old-style hotkey interface\n" ); + } + +if (dmi_results.err) { + err = dmi_results.err; + goto err_free_dev; + } + +if (dmi_results.keymap) { + dell_new_hk_type = true; - err =sparse_keymap_setup (dell_wmi_input_dev, keymap,NULL ); + err =sparse_keymap_setup (dell_wmi_input_dev, + dmi_results.keymap,NULL );/* * Sparse keymap library makes a copy of keymap so we * don't need the original one that was allocated. */ -kfree (keymap); +kfree (dmi_results.keymap); }else { err =sparse_keymap_setup (dell_wmi_input_dev, dell_wmi_legacy_keymap,NULL );
@@ -412,15 +441,6 @@ static void dell_wmi_input_destroy(void)input_unregister_device (dell_wmi_input_dev); } -static void __init find_hk_type (const struct dmi_header *dm,void *dummy) -{ -if (dm->type ==0xb2 && dm->length >6 ) { - dell_new_hk_type = true; - dell_bios_hotkey_table = - container_of(dm, struct dell_bios_hotkey_table, header); - } -} -static int __init dell_wmi_init (void ) {int err;
@@ -431,8 +451,6 @@ static int __init dell_wmi_init(void)return -ENODEV ; } -dmi_walk (find_hk_type,NULL ); - err =dell_wmi_input_setup ();if (err)return err;
Signature · 1 line
2.5.0
PATCH v3 2/3 dell-wmi, dell-laptop: select DMI
In reply to Andy Lutomirskidell-wmi and dell-laptop will compile but won't work right if DMI isn't selected.
---
Changes from v1:
- New patch.
drivers/platform/x86/Kconfig | 2 ++
1 file changed, 2 insertions(+)
drivers/platform/x86/Kconfig +2 -0
@@ -102,6 +102,7 @@ config DELL_LAPTOP select POWER_SUPPLY select LEDS_CLASS select NEW_LEDS + select DMI default n ---help--- This driver adds support for rfkill and backlight control to Dell
@@ -113,6 +114,7 @@ config DELL_WMI depends on INPUT depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP + select DMI ---help--- Say Y here if you want to support WMI-based hotkeys on Dell laptops.
Signature · 1 line
2.5.0
PATCH v3 3/3 dell-wmi: Clean up hotkey table size check
In reply to Andy LutomirskiChecking the table for a minimum size of 7 bytes makes no sense: any valid hotkey table has a size that's a multiple of 4.
Clean this up: replace the hardcoded header length with a sizeof and change the check to ignore an empty hotkey table. The only behavior change is that a 7-byte table (which is nonsensical) will now be treated as absent instead of as valid but empty.
---
Changes from v2:
- Total rewrite.
drivers/platform/x86/dell-wmi.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
drivers/platform/x86/dell-wmi.c +13 -2
@@ -329,13 +329,24 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,if (results->err || results->keymap)return ;/* We already found the hotkey table. */ -if (dm->type !=0xb2 || dm->length <=6 ) +if (dm->type !=0xb2 )return ; table = container_of(dm, struct dell_bios_hotkey_table, header); - hotkey_num = (table->header.length -4 ) / + hotkey_num = (table->header.length - +sizeof (struct dell_bios_hotkey_table )) /sizeof (struct dell_bios_keymap_entry ); +if (hotkey_num <1 ) { +/* +* Historically, dell-wmi would ignore a DMI entry of +* fewer than 7 bytes. Sizes between 4 and 8 bytes are +* nonsensical (both the header and all entries are 4 +* bytes), so we approximate the old behavior by +* ignoring tables with fewer than one entry. +*/ +return ; + } keymap =kcalloc (hotkey_num +1 ,sizeof (struct key_entry ),GFP_KERNEL );if (!keymap) {
Signature · 1 line
2.5.0