diff options
| -rw-r--r-- | drivers/firmware/efivars.c | 130 | ||||
| -rw-r--r-- | tools/testing/selftests/efivarfs/efivarfs.sh | 59 |
2 files changed, 155 insertions, 34 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 7320bf891706..bea32d1ef7d5 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, |
| @@ -1345,7 +1400,6 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
| 1345 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | 1400 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; |
| 1346 | struct efivars *efivars = psi->data; | 1401 | struct efivars *efivars = psi->data; |
| 1347 | int i, ret = 0; | 1402 | int i, ret = 0; |
| 1348 | u64 storage_space, remaining_space, max_variable_size; | ||
| 1349 | efi_status_t status = EFI_NOT_FOUND; | 1403 | efi_status_t status = EFI_NOT_FOUND; |
| 1350 | unsigned long flags; | 1404 | unsigned long flags; |
| 1351 | 1405 | ||
| @@ -1365,11 +1419,11 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
| 1365 | * size: a size of logging data | 1419 | * size: a size of logging data |
| 1366 | * DUMP_NAME_LEN * 2: a maximum size of variable name | 1420 | * DUMP_NAME_LEN * 2: a maximum size of variable name |
| 1367 | */ | 1421 | */ |
| 1368 | status = efivars->ops->query_variable_info(PSTORE_EFI_ATTRIBUTES, | 1422 | |
| 1369 | &storage_space, | 1423 | status = check_var_size_locked(efivars, PSTORE_EFI_ATTRIBUTES, |
| 1370 | &remaining_space, | 1424 | size + DUMP_NAME_LEN * 2); |
| 1371 | &max_variable_size); | 1425 | |
| 1372 | if (status || remaining_space < size + DUMP_NAME_LEN * 2) { | 1426 | if (status) { |
| 1373 | spin_unlock_irqrestore(&efivars->lock, flags); | 1427 | spin_unlock_irqrestore(&efivars->lock, flags); |
| 1374 | *id = part; | 1428 | *id = part; |
| 1375 | return -ENOSPC; | 1429 | return -ENOSPC; |
| @@ -1544,6 +1598,14 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
| 1544 | return -EINVAL; | 1598 | return -EINVAL; |
| 1545 | } | 1599 | } |
| 1546 | 1600 | ||
| 1601 | status = check_var_size_locked(efivars, new_var->Attributes, | ||
| 1602 | new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); | ||
| 1603 | |||
| 1604 | if (status && status != EFI_UNSUPPORTED) { | ||
| 1605 | spin_unlock_irq(&efivars->lock); | ||
| 1606 | return efi_status_to_err(status); | ||
| 1607 | } | ||
| 1608 | |||
| 1547 | /* now *really* create the variable via EFI */ | 1609 | /* now *really* create the variable via EFI */ |
| 1548 | status = efivars->ops->set_variable(new_var->VariableName, | 1610 | status = efivars->ops->set_variable(new_var->VariableName, |
| 1549 | &new_var->VendorGuid, | 1611 | &new_var->VendorGuid, |
diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh index 880cdd5dc63f..77edcdcc016b 100644 --- a/tools/testing/selftests/efivarfs/efivarfs.sh +++ b/tools/testing/selftests/efivarfs/efivarfs.sh | |||
| @@ -125,6 +125,63 @@ test_open_unlink() | |||
| 125 | ./open-unlink $file | 125 | ./open-unlink $file |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | # test that we can create a range of filenames | ||
| 129 | test_valid_filenames() | ||
| 130 | { | ||
| 131 | local attrs='\x07\x00\x00\x00' | ||
| 132 | local ret=0 | ||
| 133 | |||
| 134 | local file_list="abc dump-type0-11-1-1362436005 1234 -" | ||
| 135 | for f in $file_list; do | ||
| 136 | local file=$efivarfs_mount/$f-$test_guid | ||
| 137 | |||
| 138 | printf "$attrs\x00" > $file | ||
| 139 | |||
| 140 | if [ ! -e $file ]; then | ||
| 141 | echo "$file could not be created" >&2 | ||
| 142 | ret=1 | ||
| 143 | else | ||
| 144 | rm $file | ||
| 145 | fi | ||
| 146 | done | ||
| 147 | |||
| 148 | exit $ret | ||
| 149 | } | ||
| 150 | |||
| 151 | test_invalid_filenames() | ||
| 152 | { | ||
| 153 | local attrs='\x07\x00\x00\x00' | ||
| 154 | local ret=0 | ||
| 155 | |||
| 156 | local file_list=" | ||
| 157 | -1234-1234-1234-123456789abc | ||
| 158 | foo | ||
| 159 | foo-bar | ||
| 160 | -foo- | ||
| 161 | foo-barbazba-foob-foob-foob-foobarbazfoo | ||
| 162 | foo------------------------------------- | ||
| 163 | -12345678-1234-1234-1234-123456789abc | ||
| 164 | a-12345678=1234-1234-1234-123456789abc | ||
| 165 | a-12345678-1234=1234-1234-123456789abc | ||
| 166 | a-12345678-1234-1234=1234-123456789abc | ||
| 167 | a-12345678-1234-1234-1234=123456789abc | ||
| 168 | 1112345678-1234-1234-1234-123456789abc" | ||
| 169 | |||
| 170 | for f in $file_list; do | ||
| 171 | local file=$efivarfs_mount/$f | ||
| 172 | |||
| 173 | printf "$attrs\x00" 2>/dev/null > $file | ||
| 174 | |||
| 175 | if [ -e $file ]; then | ||
| 176 | echo "Creating $file should have failed" >&2 | ||
| 177 | rm $file | ||
| 178 | ret=1 | ||
| 179 | fi | ||
| 180 | done | ||
| 181 | |||
| 182 | exit $ret | ||
| 183 | } | ||
| 184 | |||
| 128 | check_prereqs | 185 | check_prereqs |
| 129 | 186 | ||
| 130 | rc=0 | 187 | rc=0 |
| @@ -135,5 +192,7 @@ run_test test_create_read | |||
| 135 | run_test test_delete | 192 | run_test test_delete |
| 136 | run_test test_zero_size_delete | 193 | run_test test_zero_size_delete |
| 137 | run_test test_open_unlink | 194 | run_test test_open_unlink |
| 195 | run_test test_valid_filenames | ||
| 196 | run_test test_invalid_filenames | ||
| 138 | 197 | ||
| 139 | exit $rc | 198 | exit $rc |
