Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Matthew Garrett and committed by
Matt Fleming
68d92986 6dbe51c2

+79 -27
+79 -27
drivers/firmware/efivars.c
··· 426 426 return status; 427 427 } 428 428 429 + static efi_status_t 430 + check_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 + 454 + static efi_status_t 455 + check_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 + 429 467 static ssize_t 430 468 efivar_guid_read(struct efivar_entry *entry, char *buf) 431 469 { ··· 585 547 } 586 548 587 549 spin_lock_irq(&efivars->lock); 588 - status = efivars->ops->set_variable(new_var->VariableName, 589 - &new_var->VendorGuid, 590 - new_var->Attributes, 591 - new_var->DataSize, 592 - new_var->Data); 550 + 551 + status = check_var_size_locked(efivars, new_var->Attributes, 552 + new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); 553 + 554 + if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED) 555 + status = efivars->ops->set_variable(new_var->VariableName, 556 + &new_var->VendorGuid, 557 + new_var->Attributes, 558 + new_var->DataSize, 559 + new_var->Data); 593 560 594 561 spin_unlock_irq(&efivars->lock); 595 562 ··· 745 702 u32 attributes; 746 703 struct inode *inode = file->f_mapping->host; 747 704 unsigned long datasize = count - sizeof(attributes); 748 - unsigned long newdatasize; 749 - u64 storage_size, remaining_size, max_size; 705 + unsigned long newdatasize, varsize; 750 706 ssize_t bytes = 0; 751 707 752 708 if (count < sizeof(attributes)) ··· 764 722 * amounts of memory. Pick a default size of 64K if 765 723 * QueryVariableInfo() isn't supported by the firmware. 766 724 */ 767 - spin_lock_irq(&efivars->lock); 768 725 769 - if (!efivars->ops->query_variable_info) 770 - status = EFI_UNSUPPORTED; 771 - else { 772 - const struct efivar_operations *fops = efivars->ops; 773 - status = fops->query_variable_info(attributes, &storage_size, 774 - &remaining_size, &max_size); 775 - } 776 - 777 - spin_unlock_irq(&efivars->lock); 726 + varsize = datasize + utf16_strsize(var->var.VariableName, 1024); 727 + status = check_var_size(efivars, attributes, varsize); 778 728 779 729 if (status != EFI_SUCCESS) { 780 730 if (status != EFI_UNSUPPORTED) 781 731 return efi_status_to_err(status); 782 732 783 - remaining_size = 65536; 733 + if (datasize > 65536) 734 + return -ENOSPC; 784 735 } 785 - 786 - if (datasize > remaining_size) 787 - return -ENOSPC; 788 736 789 737 data = kmalloc(datasize, GFP_KERNEL); 790 738 if (!data) ··· 796 764 * list (in the case of an authenticated delete). 797 765 */ 798 766 spin_lock_irq(&efivars->lock); 767 + 768 + /* 769 + * Ensure that the available space hasn't shrunk below the safe level 770 + */ 771 + 772 + status = check_var_size_locked(efivars, attributes, varsize); 773 + 774 + if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) { 775 + spin_unlock_irq(&efivars->lock); 776 + kfree(data); 777 + 778 + return efi_status_to_err(status); 779 + } 799 780 800 781 status = efivars->ops->set_variable(var->var.VariableName, 801 782 &var->var.VendorGuid, ··· 1390 1345 efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 1391 1346 struct efivars *efivars = psi->data; 1392 1347 int i, ret = 0; 1393 - u64 storage_space, remaining_space, max_variable_size; 1394 1348 efi_status_t status = EFI_NOT_FOUND; 1395 1349 unsigned long flags; 1396 1350 ··· 1409 1365 * size: a size of logging data 1410 1366 * DUMP_NAME_LEN * 2: a maximum size of variable name 1411 1367 */ 1412 - status = efivars->ops->query_variable_info(PSTORE_EFI_ATTRIBUTES, 1413 - &storage_space, 1414 - &remaining_space, 1415 - &max_variable_size); 1416 - if (status || remaining_space < size + DUMP_NAME_LEN * 2) { 1368 + 1369 + status = check_var_size_locked(efivars, PSTORE_EFI_ATTRIBUTES, 1370 + size + DUMP_NAME_LEN * 2); 1371 + 1372 + if (status) { 1417 1373 spin_unlock_irqrestore(&efivars->lock, flags); 1418 1374 *id = part; 1419 1375 return -ENOSPC; ··· 1586 1542 if (found) { 1587 1543 spin_unlock_irq(&efivars->lock); 1588 1544 return -EINVAL; 1545 + } 1546 + 1547 + status = check_var_size_locked(efivars, new_var->Attributes, 1548 + new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); 1549 + 1550 + if (status && status != EFI_UNSUPPORTED) { 1551 + spin_unlock_irq(&efivars->lock); 1552 + return efi_status_to_err(status); 1589 1553 } 1590 1554 1591 1555 /* now *really* create the variable via EFI */