Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18b6f80f authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Darren Hart
Browse files

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: default avatarAndy Lutomirski <luto@kernel.org>
Acked-by: default avatarPali Rohár <pali.rohar@gmail.com>
Signed-off-by: default avatarDarren Hart <dvhart@linux.intel.com>
parent 0c41a08e
Loading
Loading
Loading
Loading
+46 −28
Original line number Diff line number Diff line
@@ -120,7 +120,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 = {
@@ -337,20 +340,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 <
@@ -379,11 +396,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();
@@ -394,20 +412,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;
	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;
	}

		err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
	if (dmi_results.keymap) {
		dell_new_hk_type = true;

		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);
@@ -434,15 +463,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);
	}
}

/*
 * Descriptor buffer is 128 byte long and contains:
 *
@@ -524,8 +544,6 @@ static int __init dell_wmi_init(void)
	if (err)
		return err;

	dmi_walk(find_hk_type, NULL);

	err = dell_wmi_input_setup();
	if (err)
		return err;