diff options
author | Matthew Garrett <mjg@redhat.com> | 2012-05-03 16:50:46 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-07 08:56:38 -0700 |
commit | ecc53109a021559a49beb81c9c4c859012ee975e (patch) | |
tree | af0bceb7778e6a23bfff8075e6f8408bb06d7b06 | |
parent | 173c412ef8f37d781eb90f5cc8eeab118b987e68 (diff) | |
download | linux-stable-ecc53109a021559a49beb81c9c4c859012ee975e.tar.gz linux-stable-ecc53109a021559a49beb81c9c4c859012ee975e.tar.bz2 linux-stable-ecc53109a021559a49beb81c9c4c859012ee975e.zip |
efivars: Improve variable validation
commit 54b3a4d311c98ad94b737802a8b5f2c8c6bfd627 upstream.
Ben Hutchings pointed out that the validation in efivars was inadequate -
most obviously, an entry with size 0 would server as a DoS against the
kernel. Improve this based on his suggestions.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/firmware/efivars.c | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index a15c0d45f0a3..e27d56c7cc19 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -167,18 +167,21 @@ utf16_strsize(efi_char16_t *data, unsigned long maxlength) } static bool -validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len) +validate_device_path(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) { struct efi_generic_dev_path *node; int offset = 0; node = (struct efi_generic_dev_path *)buffer; - while (offset < len) { - offset += node->length; + if (len < sizeof(*node)) + return false; - if (offset > len) - return false; + while (offset <= len - sizeof(*node) && + node->length >= sizeof(*node) && + node->length <= len - offset) { + offset += node->length; if ((node->type == EFI_DEV_END_PATH || node->type == EFI_DEV_END_PATH2) && @@ -197,7 +200,8 @@ validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len) } static bool -validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len) +validate_boot_order(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) { /* An array of 16-bit integers */ if ((len % 2) != 0) @@ -207,19 +211,27 @@ validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len) } static bool -validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) +validate_load_option(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) { u16 filepathlength; - int i, desclength = 0; + int i, desclength = 0, namelen; + + namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName)); /* Either "Boot" or "Driver" followed by four digits of hex */ for (i = match; i < match+4; i++) { - if (hex_to_bin(var->VariableName[i] & 0xff) < 0) + if (var->VariableName[i] > 127 || + hex_to_bin(var->VariableName[i] & 0xff) < 0) return true; } - /* A valid entry must be at least 6 bytes */ - if (len < 6) + /* Reject it if there's 4 digits of hex and then further content */ + if (namelen > match + 4) + return false; + + /* A valid entry must be at least 8 bytes */ + if (len < 8) return false; filepathlength = buffer[4] | buffer[5] << 8; @@ -228,7 +240,7 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) * There's no stored length for the description, so it has to be * found by hand */ - desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len) + 2; + desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; /* Each boot entry must have a descriptor */ if (!desclength) @@ -250,7 +262,8 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) } static bool -validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len) +validate_uint16(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) { /* A single 16-bit integer */ if (len != 2) @@ -260,7 +273,8 @@ validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len) } static bool -validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len) +validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, + unsigned long len) { int i; @@ -278,7 +292,7 @@ validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len) struct variable_validate { char *name; bool (*validate)(struct efi_variable *var, int match, u8 *data, - int len); + unsigned long len); }; static const struct variable_validate variable_validate[] = { @@ -300,7 +314,7 @@ static const struct variable_validate variable_validate[] = { }; static bool -validate_var(struct efi_variable *var, u8 *data, int len) +validate_var(struct efi_variable *var, u8 *data, unsigned long len) { int i; u16 *unicode_name = var->VariableName; |