aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Garrett <matthew.garrett@nebula.com>2013-03-02 19:40:17 -0500
committerMatt Fleming <matt.fleming@intel.com>2013-03-06 09:43:29 -0500
commit68d929862e29a8b52a7f2f2f86a0600423b093cd (patch)
tree2ae86a0b511deea1fd79b375297eedde45921a03
parent6dbe51c251a327e012439c4772097a13df43c5b8 (diff)
efi: be more paranoid about available space when creating variables
UEFI variables are typically stored in flash. For various reasons, avaiable space is typically not reclaimed immediately upon the deletion of a variable - instead, the system will garbage collect during initialisation after a reboot. Some systems appear to handle this garbage collection extremely poorly, failing if more than 50% of the system flash is in use. This can result in the machine refusing to boot. The safest thing to do for the moment is to forbid writes if they'd end up using more than half of the storage space. We can make this more finegrained later if we come up with a method for identifying the broken machines. Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com> Cc: Josh Boyer <jwboyer@redhat.com> Cc: <stable@vger.kernel.org> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--drivers/firmware/efivars.c106
1 files changed, 79 insertions, 27 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 7320bf891706..0d504971ed4c 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -426,6 +426,44 @@ get_var_data(struct efivars *efivars, struct efi_variable *var)
426 return status; 426 return status;
427} 427}
428 428
429static efi_status_t
430check_var_size_locked(struct efivars *efivars, u32 attributes,
431 unsigned long size)
432{
433 u64 storage_size, remaining_size, max_size;
434 efi_status_t status;
435 const struct efivar_operations *fops = efivars->ops;
436
437 if (!efivars->ops->query_variable_info)
438 return EFI_UNSUPPORTED;
439
440 status = fops->query_variable_info(attributes, &storage_size,
441 &remaining_size, &max_size);
442
443 if (status != EFI_SUCCESS)
444 return status;
445
446 if (!storage_size || size > remaining_size || size > max_size ||
447 (remaining_size - size) < (storage_size / 2))
448 return EFI_OUT_OF_RESOURCES;
449
450 return status;
451}
452
453
454static efi_status_t
455check_var_size(struct efivars *efivars, u32 attributes, unsigned long size)
456{
457 efi_status_t status;
458 unsigned long flags;
459
460 spin_lock_irqsave(&efivars->lock, flags);
461 status = check_var_size_locked(efivars, attributes, size);
462 spin_unlock_irqrestore(&efivars->lock, flags);
463
464 return status;
465}
466
429static ssize_t 467static ssize_t
430efivar_guid_read(struct efivar_entry *entry, char *buf) 468efivar_guid_read(struct efivar_entry *entry, char *buf)
431{ 469{
@@ -547,11 +585,16 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
547 } 585 }
548 586
549 spin_lock_irq(&efivars->lock); 587 spin_lock_irq(&efivars->lock);
550 status = efivars->ops->set_variable(new_var->VariableName, 588
551 &new_var->VendorGuid, 589 status = check_var_size_locked(efivars, new_var->Attributes,
552 new_var->Attributes, 590 new_var->DataSize + utf16_strsize(new_var->VariableName, 1024));
553 new_var->DataSize, 591
554 new_var->Data); 592 if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED)
593 status = efivars->ops->set_variable(new_var->VariableName,
594 &new_var->VendorGuid,
595 new_var->Attributes,
596 new_var->DataSize,
597 new_var->Data);
555 598
556 spin_unlock_irq(&efivars->lock); 599 spin_unlock_irq(&efivars->lock);
557 600
@@ -702,8 +745,7 @@ static ssize_t efivarfs_file_write(struct file *file,
702 u32 attributes; 745 u32 attributes;
703 struct inode *inode = file->f_mapping->host; 746 struct inode *inode = file->f_mapping->host;
704 unsigned long datasize = count - sizeof(attributes); 747 unsigned long datasize = count - sizeof(attributes);
705 unsigned long newdatasize; 748 unsigned long newdatasize, varsize;
706 u64 storage_size, remaining_size, max_size;
707 ssize_t bytes = 0; 749 ssize_t bytes = 0;
708 750
709 if (count < sizeof(attributes)) 751 if (count < sizeof(attributes))
@@ -722,28 +764,18 @@ static ssize_t efivarfs_file_write(struct file *file,
722 * amounts of memory. Pick a default size of 64K if 764 * amounts of memory. Pick a default size of 64K if
723 * QueryVariableInfo() isn't supported by the firmware. 765 * QueryVariableInfo() isn't supported by the firmware.
724 */ 766 */
725 spin_lock_irq(&efivars->lock);
726 767
727 if (!efivars->ops->query_variable_info) 768 varsize = datasize + utf16_strsize(var->var.VariableName, 1024);
728 status = EFI_UNSUPPORTED; 769 status = check_var_size(efivars, attributes, varsize);
729 else {
730 const struct efivar_operations *fops = efivars->ops;
731 status = fops->query_variable_info(attributes, &storage_size,
732 &remaining_size, &max_size);
733 }
734
735 spin_unlock_irq(&efivars->lock);
736 770
737 if (status != EFI_SUCCESS) { 771 if (status != EFI_SUCCESS) {
738 if (status != EFI_UNSUPPORTED) 772 if (status != EFI_UNSUPPORTED)
739 return efi_status_to_err(status); 773 return efi_status_to_err(status);
740 774
741 remaining_size = 65536; 775 if (datasize > 65536)
776 return -ENOSPC;
742 } 777 }
743 778
744 if (datasize > remaining_size)
745 return -ENOSPC;
746
747 data = kmalloc(datasize, GFP_KERNEL); 779 data = kmalloc(datasize, GFP_KERNEL);
748 if (!data) 780 if (!data)
749 return -ENOMEM; 781 return -ENOMEM;
@@ -765,6 +797,19 @@ static ssize_t efivarfs_file_write(struct file *file,
765 */ 797 */
766 spin_lock_irq(&efivars->lock); 798 spin_lock_irq(&efivars->lock);
767 799
800 /*
801 * Ensure that the available space hasn't shrunk below the safe level
802 */
803
804 status = check_var_size_locked(efivars, attributes, varsize);
805
806 if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
807 spin_unlock_irq(&efivars->lock);
808 kfree(data);
809
810 return efi_status_to_err(status);
811 }
812
768 status = efivars->ops->set_variable(var->var.VariableName, 813 status = efivars->ops->set_variable(var->var.VariableName,
769 &var->var.VendorGuid, 814 &var->var.VendorGuid,
770 attributes, datasize, 815 attributes, datasize,
@@ -1345,7 +1390,6 @@ static int efi_pstore_write(enum pstore_type_id type,
1345 efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 1390 efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
1346 struct efivars *efivars = psi->data; 1391 struct efivars *efivars = psi->data;
1347 int i, ret = 0; 1392 int i, ret = 0;
1348 u64 storage_space, remaining_space, max_variable_size;
1349 efi_status_t status = EFI_NOT_FOUND; 1393 efi_status_t status = EFI_NOT_FOUND;
1350 unsigned long flags; 1394 unsigned long flags;
1351 1395
@@ -1365,11 +1409,11 @@ static int efi_pstore_write(enum pstore_type_id type,
1365 * size: a size of logging data 1409 * size: a size of logging data
1366 * DUMP_NAME_LEN * 2: a maximum size of variable name 1410 * DUMP_NAME_LEN * 2: a maximum size of variable name
1367 */ 1411 */
1368 status = efivars->ops->query_variable_info(PSTORE_EFI_ATTRIBUTES, 1412
1369 &storage_space, 1413 status = check_var_size_locked(efivars, PSTORE_EFI_ATTRIBUTES,
1370 &remaining_space, 1414 size + DUMP_NAME_LEN * 2);
1371 &max_variable_size); 1415
1372 if (status || remaining_space < size + DUMP_NAME_LEN * 2) { 1416 if (status) {
1373 spin_unlock_irqrestore(&efivars->lock, flags); 1417 spin_unlock_irqrestore(&efivars->lock, flags);
1374 *id = part; 1418 *id = part;
1375 return -ENOSPC; 1419 return -ENOSPC;
@@ -1544,6 +1588,14 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
1544 return -EINVAL; 1588 return -EINVAL;
1545 } 1589 }
1546 1590
1591 status = check_var_size_locked(efivars, new_var->Attributes,
1592 new_var->DataSize + utf16_strsize(new_var->VariableName, 1024));
1593
1594 if (status && status != EFI_UNSUPPORTED) {
1595 spin_unlock_irq(&efivars->lock);
1596 return efi_status_to_err(status);
1597 }
1598
1547 /* now *really* create the variable via EFI */ 1599 /* now *really* create the variable via EFI */
1548 status = efivars->ops->set_variable(new_var->VariableName, 1600 status = efivars->ops->set_variable(new_var->VariableName,
1549 &new_var->VendorGuid, 1601 &new_var->VendorGuid,