efivars: only check for duplicates on the registered list
authorMatt Fleming <matt.fleming@intel.com>
Fri, 26 Apr 2013 09:10:55 +0000 (10:10 +0100)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 26 Apr 2013 15:02:03 +0000 (08:02 -0700)
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>
drivers/firmware/efivars.c

index 182ce94..f4baa11 100644 (file)
@@ -1628,10 +1628,11 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
        return count;
 }
 
-static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor)
+static bool variable_is_present(struct efivars *efivars,
+                               efi_char16_t *variable_name,
+                               efi_guid_t *vendor)
 {
        struct efivar_entry *entry, *n;
-       struct efivars *efivars = &__efivars;
        unsigned long strsize1, strsize2;
        bool found = false;
 
@@ -1703,8 +1704,8 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
                        if (status != EFI_SUCCESS) {
                                break;
                        } else {
-                               if (!variable_is_present(variable_name,
-                                   &vendor)) {
+                               if (!variable_is_present(efivars,
+                                   variable_name, &vendor)) {
                                        found = true;
                                        break;
                                }
@@ -2008,7 +2009,8 @@ int register_efivars(struct efivars *efivars,
                         * we'll ever see a different variable name,
                         * and may end up looping here forever.
                         */
-                       if (variable_is_present(variable_name, &vendor_guid)) {
+                       if (variable_is_present(efivars, variable_name,
+                                               &vendor_guid)) {
                                dup_variable_bug(variable_name, &vendor_guid,
                                                 variable_name_size);
                                status = EFI_NOT_FOUND;