diff options
author | Matt Domsch <Matt_Domsch@dell.com> | 2007-01-26 03:57:18 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-01-26 16:51:01 -0500 |
commit | 496a0fc8c5572a626de41d56d7c7ed005a2c1b48 (patch) | |
tree | 28039f07573e7f935d1e7b9e3a9aa5354b91d233 /drivers/firmware | |
parent | 01f2073411e01777e3c6f45a4bf05ea76493f326 (diff) |
[PATCH] Fix race in efi variable delete code
Fix race when deleting an EFI variable and issuing another EFI command on
the same variable. The removal of the variable from the efivars_list
should be done in efivar_delete and not delayed until the kobject release.
Furthermore, remove the item from the list at module unload time, and use
list_for_each_entry_safe() rather than list_for_each_safe() for
readability.
Tested on ia64.
Signed-off-by: Prarit Bhargava <prarit@redhat.com>
Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efivars.c | 29 |
1 files changed, 12 insertions, 17 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 5ab5e393b882..c6281ccd4fe7 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -122,8 +122,6 @@ struct efivar_entry { | |||
122 | struct kobject kobj; | 122 | struct kobject kobj; |
123 | }; | 123 | }; |
124 | 124 | ||
125 | #define get_efivar_entry(n) list_entry(n, struct efivar_entry, list) | ||
126 | |||
127 | struct efivar_attribute { | 125 | struct efivar_attribute { |
128 | struct attribute attr; | 126 | struct attribute attr; |
129 | ssize_t (*show) (struct efivar_entry *entry, char *buf); | 127 | ssize_t (*show) (struct efivar_entry *entry, char *buf); |
@@ -386,9 +384,6 @@ static struct sysfs_ops efivar_attr_ops = { | |||
386 | static void efivar_release(struct kobject *kobj) | 384 | static void efivar_release(struct kobject *kobj) |
387 | { | 385 | { |
388 | struct efivar_entry *var = container_of(kobj, struct efivar_entry, kobj); | 386 | struct efivar_entry *var = container_of(kobj, struct efivar_entry, kobj); |
389 | spin_lock(&efivars_lock); | ||
390 | list_del(&var->list); | ||
391 | spin_unlock(&efivars_lock); | ||
392 | kfree(var); | 387 | kfree(var); |
393 | } | 388 | } |
394 | 389 | ||
@@ -430,9 +425,8 @@ static ssize_t | |||
430 | efivar_create(struct subsystem *sub, const char *buf, size_t count) | 425 | efivar_create(struct subsystem *sub, const char *buf, size_t count) |
431 | { | 426 | { |
432 | struct efi_variable *new_var = (struct efi_variable *)buf; | 427 | struct efi_variable *new_var = (struct efi_variable *)buf; |
433 | struct efivar_entry *search_efivar = NULL; | 428 | struct efivar_entry *search_efivar, *n; |
434 | unsigned long strsize1, strsize2; | 429 | unsigned long strsize1, strsize2; |
435 | struct list_head *pos, *n; | ||
436 | efi_status_t status = EFI_NOT_FOUND; | 430 | efi_status_t status = EFI_NOT_FOUND; |
437 | int found = 0; | 431 | int found = 0; |
438 | 432 | ||
@@ -444,8 +438,7 @@ efivar_create(struct subsystem *sub, const char *buf, size_t count) | |||
444 | /* | 438 | /* |
445 | * Does this variable already exist? | 439 | * Does this variable already exist? |
446 | */ | 440 | */ |
447 | list_for_each_safe(pos, n, &efivar_list) { | 441 | list_for_each_entry_safe(search_efivar, n, &efivar_list, list) { |
448 | search_efivar = get_efivar_entry(pos); | ||
449 | strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); | 442 | strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); |
450 | strsize2 = utf8_strsize(new_var->VariableName, 1024); | 443 | strsize2 = utf8_strsize(new_var->VariableName, 1024); |
451 | if (strsize1 == strsize2 && | 444 | if (strsize1 == strsize2 && |
@@ -490,9 +483,8 @@ static ssize_t | |||
490 | efivar_delete(struct subsystem *sub, const char *buf, size_t count) | 483 | efivar_delete(struct subsystem *sub, const char *buf, size_t count) |
491 | { | 484 | { |
492 | struct efi_variable *del_var = (struct efi_variable *)buf; | 485 | struct efi_variable *del_var = (struct efi_variable *)buf; |
493 | struct efivar_entry *search_efivar = NULL; | 486 | struct efivar_entry *search_efivar, *n; |
494 | unsigned long strsize1, strsize2; | 487 | unsigned long strsize1, strsize2; |
495 | struct list_head *pos, *n; | ||
496 | efi_status_t status = EFI_NOT_FOUND; | 488 | efi_status_t status = EFI_NOT_FOUND; |
497 | int found = 0; | 489 | int found = 0; |
498 | 490 | ||
@@ -504,8 +496,7 @@ efivar_delete(struct subsystem *sub, const char *buf, size_t count) | |||
504 | /* | 496 | /* |
505 | * Does this variable already exist? | 497 | * Does this variable already exist? |
506 | */ | 498 | */ |
507 | list_for_each_safe(pos, n, &efivar_list) { | 499 | list_for_each_entry_safe(search_efivar, n, &efivar_list, list) { |
508 | search_efivar = get_efivar_entry(pos); | ||
509 | strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); | 500 | strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); |
510 | strsize2 = utf8_strsize(del_var->VariableName, 1024); | 501 | strsize2 = utf8_strsize(del_var->VariableName, 1024); |
511 | if (strsize1 == strsize2 && | 502 | if (strsize1 == strsize2 && |
@@ -537,9 +528,9 @@ efivar_delete(struct subsystem *sub, const char *buf, size_t count) | |||
537 | spin_unlock(&efivars_lock); | 528 | spin_unlock(&efivars_lock); |
538 | return -EIO; | 529 | return -EIO; |
539 | } | 530 | } |
531 | list_del(&search_efivar->list); | ||
540 | /* We need to release this lock before unregistering. */ | 532 | /* We need to release this lock before unregistering. */ |
541 | spin_unlock(&efivars_lock); | 533 | spin_unlock(&efivars_lock); |
542 | |||
543 | efivar_unregister(search_efivar); | 534 | efivar_unregister(search_efivar); |
544 | 535 | ||
545 | /* It's dead Jim.... */ | 536 | /* It's dead Jim.... */ |
@@ -768,10 +759,14 @@ out_free: | |||
768 | static void __exit | 759 | static void __exit |
769 | efivars_exit(void) | 760 | efivars_exit(void) |
770 | { | 761 | { |
771 | struct list_head *pos, *n; | 762 | struct efivar_entry *entry, *n; |
772 | 763 | ||
773 | list_for_each_safe(pos, n, &efivar_list) | 764 | list_for_each_entry_safe(entry, n, &efivar_list, list) { |
774 | efivar_unregister(get_efivar_entry(pos)); | 765 | spin_lock(&efivars_lock); |
766 | list_del(&entry->list); | ||
767 | spin_unlock(&efivars_lock); | ||
768 | efivar_unregister(entry); | ||
769 | } | ||
775 | 770 | ||
776 | subsystem_unregister(&vars_subsys); | 771 | subsystem_unregister(&vars_subsys); |
777 | firmware_unregister(&efi_subsys); | 772 | firmware_unregister(&efi_subsys); |