diff options
Diffstat (limited to 'drivers/firmware/efivars.c')
-rw-r--r-- | drivers/firmware/efivars.c | 131 |
1 files changed, 97 insertions, 34 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 7320bf891706..fe62aa392239 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 | |||
727 | if (!efivars->ops->query_variable_info) | ||
728 | status = EFI_UNSUPPORTED; | ||
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 | 767 | ||
735 | spin_unlock_irq(&efivars->lock); | 768 | varsize = datasize + utf16_strsize(var->var.VariableName, 1024); |
769 | status = check_var_size(efivars, attributes, varsize); | ||
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, |
@@ -929,8 +974,8 @@ static bool efivarfs_valid_name(const char *str, int len) | |||
929 | if (len < GUID_LEN + 2) | 974 | if (len < GUID_LEN + 2) |
930 | return false; | 975 | return false; |
931 | 976 | ||
932 | /* GUID should be right after the first '-' */ | 977 | /* GUID must be preceded by a '-' */ |
933 | if (s - 1 != strchr(str, '-')) | 978 | if (*(s - 1) != '-') |
934 | return false; | 979 | return false; |
935 | 980 | ||
936 | /* | 981 | /* |
@@ -1118,15 +1163,22 @@ static struct dentry_operations efivarfs_d_ops = { | |||
1118 | 1163 | ||
1119 | static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) | 1164 | static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) |
1120 | { | 1165 | { |
1166 | struct dentry *d; | ||
1121 | struct qstr q; | 1167 | struct qstr q; |
1168 | int err; | ||
1122 | 1169 | ||
1123 | q.name = name; | 1170 | q.name = name; |
1124 | q.len = strlen(name); | 1171 | q.len = strlen(name); |
1125 | 1172 | ||
1126 | if (efivarfs_d_hash(NULL, NULL, &q)) | 1173 | err = efivarfs_d_hash(NULL, NULL, &q); |
1127 | return NULL; | 1174 | if (err) |
1175 | return ERR_PTR(err); | ||
1176 | |||
1177 | d = d_alloc(parent, &q); | ||
1178 | if (d) | ||
1179 | return d; | ||
1128 | 1180 | ||
1129 | return d_alloc(parent, &q); | 1181 | return ERR_PTR(-ENOMEM); |
1130 | } | 1182 | } |
1131 | 1183 | ||
1132 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | 1184 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) |
@@ -1136,6 +1188,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
1136 | struct efivar_entry *entry, *n; | 1188 | struct efivar_entry *entry, *n; |
1137 | struct efivars *efivars = &__efivars; | 1189 | struct efivars *efivars = &__efivars; |
1138 | char *name; | 1190 | char *name; |
1191 | int err = -ENOMEM; | ||
1139 | 1192 | ||
1140 | efivarfs_sb = sb; | 1193 | efivarfs_sb = sb; |
1141 | 1194 | ||
@@ -1186,8 +1239,10 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
1186 | goto fail_name; | 1239 | goto fail_name; |
1187 | 1240 | ||
1188 | dentry = efivarfs_alloc_dentry(root, name); | 1241 | dentry = efivarfs_alloc_dentry(root, name); |
1189 | if (!dentry) | 1242 | if (IS_ERR(dentry)) { |
1243 | err = PTR_ERR(dentry); | ||
1190 | goto fail_inode; | 1244 | goto fail_inode; |
1245 | } | ||
1191 | 1246 | ||
1192 | /* copied by the above to local storage in the dentry. */ | 1247 | /* copied by the above to local storage in the dentry. */ |
1193 | kfree(name); | 1248 | kfree(name); |
@@ -1214,7 +1269,7 @@ fail_inode: | |||
1214 | fail_name: | 1269 | fail_name: |
1215 | kfree(name); | 1270 | kfree(name); |
1216 | fail: | 1271 | fail: |
1217 | return -ENOMEM; | 1272 | return err; |
1218 | } | 1273 | } |
1219 | 1274 | ||
1220 | static struct dentry *efivarfs_mount(struct file_system_type *fs_type, | 1275 | static struct dentry *efivarfs_mount(struct file_system_type *fs_type, |
@@ -1234,6 +1289,7 @@ static struct file_system_type efivarfs_type = { | |||
1234 | .mount = efivarfs_mount, | 1289 | .mount = efivarfs_mount, |
1235 | .kill_sb = efivarfs_kill_sb, | 1290 | .kill_sb = efivarfs_kill_sb, |
1236 | }; | 1291 | }; |
1292 | MODULE_ALIAS_FS("efivarfs"); | ||
1237 | 1293 | ||
1238 | /* | 1294 | /* |
1239 | * Handle negative dentry. | 1295 | * Handle negative dentry. |
@@ -1345,7 +1401,6 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
1345 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | 1401 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; |
1346 | struct efivars *efivars = psi->data; | 1402 | struct efivars *efivars = psi->data; |
1347 | int i, ret = 0; | 1403 | int i, ret = 0; |
1348 | u64 storage_space, remaining_space, max_variable_size; | ||
1349 | efi_status_t status = EFI_NOT_FOUND; | 1404 | efi_status_t status = EFI_NOT_FOUND; |
1350 | unsigned long flags; | 1405 | unsigned long flags; |
1351 | 1406 | ||
@@ -1365,11 +1420,11 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
1365 | * size: a size of logging data | 1420 | * size: a size of logging data |
1366 | * DUMP_NAME_LEN * 2: a maximum size of variable name | 1421 | * DUMP_NAME_LEN * 2: a maximum size of variable name |
1367 | */ | 1422 | */ |
1368 | status = efivars->ops->query_variable_info(PSTORE_EFI_ATTRIBUTES, | 1423 | |
1369 | &storage_space, | 1424 | status = check_var_size_locked(efivars, PSTORE_EFI_ATTRIBUTES, |
1370 | &remaining_space, | 1425 | size + DUMP_NAME_LEN * 2); |
1371 | &max_variable_size); | 1426 | |
1372 | if (status || remaining_space < size + DUMP_NAME_LEN * 2) { | 1427 | if (status) { |
1373 | spin_unlock_irqrestore(&efivars->lock, flags); | 1428 | spin_unlock_irqrestore(&efivars->lock, flags); |
1374 | *id = part; | 1429 | *id = part; |
1375 | return -ENOSPC; | 1430 | return -ENOSPC; |
@@ -1544,6 +1599,14 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
1544 | return -EINVAL; | 1599 | return -EINVAL; |
1545 | } | 1600 | } |
1546 | 1601 | ||
1602 | status = check_var_size_locked(efivars, new_var->Attributes, | ||
1603 | new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); | ||
1604 | |||
1605 | if (status && status != EFI_UNSUPPORTED) { | ||
1606 | spin_unlock_irq(&efivars->lock); | ||
1607 | return efi_status_to_err(status); | ||
1608 | } | ||
1609 | |||
1547 | /* now *really* create the variable via EFI */ | 1610 | /* now *really* create the variable via EFI */ |
1548 | status = efivars->ops->set_variable(new_var->VariableName, | 1611 | status = efivars->ops->set_variable(new_var->VariableName, |
1549 | &new_var->VendorGuid, | 1612 | &new_var->VendorGuid, |