[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1335930906.8274.97.camel@deadeye>
Date: Wed, 02 May 2012 04:55:06 +0100
From: Ben Hutchings <ben@...adent.org.uk>
To: Matthew Garrett <mjg@...hat.com>
Cc: torvalds@...ux-foundation.org, linux-kernel@...r.kernel.org,
stable@...r.kernel.org
Subject: Re: [PATCH 2/2] efi: Validate UEFI boot variables
On Mon, 2012-04-30 at 16:11 -0400, Matthew Garrett wrote:
> A common flaw in UEFI systems is a refusal to POST triggered by a malformed
> boot variable. Once in this state, machines may only be restored by
> reflashing their firmware with an external hardware device. While this is
> obviously a firmware bug, the serious nature of the outcome suggests that
> operating systems should filter their variable writes in order to prevent
> a malicious user from rendering the machine unusable.
[...]
> +static bool
> +validate_device_path(struct efi_variable *var, int match, u8 *buffer,
> int len)
> +{
> + struct efi_generic_dev_path *node;
> + int offset = 0;
> +
> + node = (struct efi_generic_dev_path *)buffer;
> +
> + while (offset < len) {
> + offset += node->length;
> +
> + if (offset > len)
> + return false;
> +
> + if ((node->type == EFI_DEV_END_PATH ||
> + node->type == EFI_DEV_END_PATH2) &&
> + node->sub_type == EFI_DEV_END_ENTIRE)
> + return true;
> +
> + node = (struct efi_generic_dev_path *)(buffer + offset);
> + }
This validation is crap; you're not accounting for the size of the node
or invalid lengths. Try:
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) &&
node->sub_type == EFI_DEV_END_ENTIRE)
return true;
node = (struct efi_generic_dev_path *)(buffer + offset);
}
[...]
> +static bool
> +validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len)
> +{
> + u16 filepathlength;
> + int i, desclength = 0;
> +
> + /* 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)
> + return true;
> + }
Isn't this slightly too restrictive? The '& 0xff' results in many
non-ASCII characters aliasing hex digits and potentially causes a
variable to be validated as if it was special. I would think the
correct condition is:
if (var->VariableName[i] > 127 ||
hex_to_bin(var->VariableName[i]) < 0)
Presumably the variable should also be ignored if there are any more
characters after the 4 hex digits?
> + /* A valid entry must be at least 6 bytes */
> + if (len < 6)
> + return false;
Surely 8 bytes - otherwise you don't even have space for the
description's null terminator.
> + filepathlength = buffer[4] | buffer[5] << 8;
> +
> + /*
> + * 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;
[...]
Second argument should be len - 6.
Ben.
--
Ben Hutchings
Design a system any fool can use, and only a fool will want to use it.
Download attachment "signature.asc" of type "application/pgp-signature" (829 bytes)
Powered by blists - more mailing lists