diff options
author | Matt Fleming <matt.fleming@intel.com> | 2013-04-26 05:10:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-26 11:02:03 -0400 |
commit | f464246d85d5a5c0fdbf5838b8c58ef59bd82fcc (patch) | |
tree | e0788b40695dcaa64ef2baa2367639dcdb3f4fc5 | |
parent | 37b7f3c76595e23257f61bd80b223de8658617ee (diff) |
efivars: only check for duplicates on the registered list
variable_is_present() accesses '__efivars' directly, but when called via
gsmi_init() Michel reports observing the following crash,
BUG: unable to handle kernel NULL pointer dereference at (null)
IP: variable_is_present+0x55/0x170
Call Trace:
register_efivars+0x106/0x370
gsmi_init+0x2ad/0x3da
do_one_initcall+0x3f/0x170
The reason for the crash is that '__efivars' hasn't been initialised nor
has it been registered with register_efivars() by the time the google
EFI SMI driver runs. The gsmi code uses its own struct efivars, and
therefore, a different variable list. Fix the above crash by passing
the registered struct efivars to variable_is_present(), so that we
traverse the correct list.
Reported-by: Michel Lespinasse <walken@google.com>
Tested-by: Michel Lespinasse <walken@google.com>
Cc: Mike Waychison <mikew@google.com>
Cc: Matthew Garrett <matthew.garrett@nebula.com>
Cc: Seiji Aguchi <seiji.aguchi@hds.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/firmware/efivars.c | 12 |
1 files changed, 7 insertions, 5 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 182ce9471175..f4baa11d3644 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -1628,10 +1628,11 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | |||
1628 | return count; | 1628 | return count; |
1629 | } | 1629 | } |
1630 | 1630 | ||
1631 | static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor) | 1631 | static bool variable_is_present(struct efivars *efivars, |
1632 | efi_char16_t *variable_name, | ||
1633 | efi_guid_t *vendor) | ||
1632 | { | 1634 | { |
1633 | struct efivar_entry *entry, *n; | 1635 | struct efivar_entry *entry, *n; |
1634 | struct efivars *efivars = &__efivars; | ||
1635 | unsigned long strsize1, strsize2; | 1636 | unsigned long strsize1, strsize2; |
1636 | bool found = false; | 1637 | bool found = false; |
1637 | 1638 | ||
@@ -1703,8 +1704,8 @@ static void efivar_update_sysfs_entries(struct work_struct *work) | |||
1703 | if (status != EFI_SUCCESS) { | 1704 | if (status != EFI_SUCCESS) { |
1704 | break; | 1705 | break; |
1705 | } else { | 1706 | } else { |
1706 | if (!variable_is_present(variable_name, | 1707 | if (!variable_is_present(efivars, |
1707 | &vendor)) { | 1708 | variable_name, &vendor)) { |
1708 | found = true; | 1709 | found = true; |
1709 | break; | 1710 | break; |
1710 | } | 1711 | } |
@@ -2008,7 +2009,8 @@ int register_efivars(struct efivars *efivars, | |||
2008 | * we'll ever see a different variable name, | 2009 | * we'll ever see a different variable name, |
2009 | * and may end up looping here forever. | 2010 | * and may end up looping here forever. |
2010 | */ | 2011 | */ |
2011 | if (variable_is_present(variable_name, &vendor_guid)) { | 2012 | if (variable_is_present(efivars, variable_name, |
2013 | &vendor_guid)) { | ||
2012 | dup_variable_bug(variable_name, &vendor_guid, | 2014 | dup_variable_bug(variable_name, &vendor_guid, |
2013 | variable_name_size); | 2015 | variable_name_size); |
2014 | status = EFI_NOT_FOUND; | 2016 | status = EFI_NOT_FOUND; |