diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/firmware/efivars.c | 106 |
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 | ||
| 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 | static ssize_t | 467 | static ssize_t |
| 430 | efivar_guid_read(struct efivar_entry *entry, char *buf) | 468 | efivar_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, |
