fwupd/plugins/uefi/fu-uefi-devpath.c
Richard Hughes 26d3da4074 uefi: Fix a libasan failure when reading a UEFI variable
It seems some older versions of libefi var incorrectly build the 'length' value
in the DP returned from efi_generate_file_device_path(). This means we copy past
the end of the allocated buffer when parsing the efi_update_info_t structure.

This bug seems fixed in efivar git master, and this fix is only going to help
people with older efivar versions. It's probably a good thing to be a bit more
paranoid about EFI variable data anyway.

    DEBUG: UpdateInfo:
           ? 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
    ????????????????????????????????????????????????????????????????????????????????????????????????????????
    0x0000 ? 07 00 00 00 20 d9 7b 69 cf 12 a9 4d 83 85 99 69 09 bc 65 59 00 00 05 00 00 00 00 00 00 00 00 00
    0x0020 ? 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 01 2a 00 01 00 00 00 00 08 00 00
    0x0040 ? 00 00 00 00 00 40 06 00 00 00 00 00 5a aa 97 5a 10 d5 7e 49 99 0b ca 8d 35 4d c8 6d 02 02 04 04
    0x0060 ? 86 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 6f 00 72 00 61 00 5c 00 66 00 77 00 5c 00
    0x0080 ? 66 00 77 00 75 00 70 00 64 00 2d 00 36 00 39 00 37 00 62 00 64 00 39 00 32 00 30 00 2d 00 31 00
    0x00a0 ? 32 00 63 00 66 00 2d 00 34 00 64 00 61 00 39 00 2d 00 38 00 33 00 38 00 35 00 2d 00 39 00 39 00
    0x00c0 ? 36 00 39 00 30 00 39 00 62 00 63 00 36 00 35 00 35 00 39 00 2e 00 63 00 61 00 70 00 00 00 7f ff
    0x00e0 ? 04 00
    DEBUG: DP type:0x04 subtype:0x01 size:0x002a
    DEBUG: DP type:0x04 subtype:0x04 size:0x0086
    DEBUG: found END_ENTIRE at 0x00aa
    DEBUG: DP length invalid! Truncating from 0x0086 to 0x0080
    DEBUG: DP type:0x7f subtype:0xff size:0x0004
2019-05-02 17:25:34 +01:00

129 lines
3.2 KiB
C

/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <efivar.h>
#include "fu-common.h"
#include "fu-uefi-devpath.h"
#include "fwupd-error.h"
typedef struct {
guint8 type;
guint8 subtype;
GBytes *data;
} FuUefiDevPath;
static void
fu_uefi_efi_dp_free (FuUefiDevPath *dp)
{
if (dp->data != NULL)
g_bytes_unref (dp->data);
g_free (dp);
}
GBytes *
fu_uefi_devpath_find_data (GPtrArray *dps, guint8 type, guint8 subtype, GError **error)
{
for (guint i = 0; i < dps->len; i++) {
FuUefiDevPath *dp = g_ptr_array_index (dps, i);
if (dp->type == type && dp->subtype == subtype)
return dp->data;
}
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no DP with type 0x%02x and subtype 0x%02x",
type, subtype);
return NULL;
}
GPtrArray *
fu_uefi_devpath_parse (const guint8 *buf, gsize sz,
FuUefiDevpathParseFlags flags, GError **error)
{
guint16 offset = 0;
g_autoptr(GPtrArray) dps = NULL;
/* sanity check */
if (sz < sizeof(efidp_header)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"const_efidp is corrupt");
return NULL;
}
dps = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_uefi_efi_dp_free);
while (1) {
FuUefiDevPath *dp;
const efidp_header *hdr = (efidp_header *) (buf + offset);
guint16 hdr_length = GUINT16_FROM_LE(hdr->length);
/* check if last entry */
g_debug ("DP type:0x%02x subtype:0x%02x size:0x%04x",
hdr->type, hdr->subtype, hdr->length);
if (hdr->type == EFIDP_END_TYPE && hdr->subtype == EFIDP_END_ENTIRE)
break;
/* work around a bug in efi_va_generate_file_device_path_from_esp */
if (offset + sizeof(efidp_header) + hdr->length > sz) {
hdr_length = 0;
fu_common_dump_full (G_LOG_DOMAIN, "efidp",
buf + offset, sz - offset, 32,
FU_DUMP_FLAGS_SHOW_ADDRESSES);
for (guint16 i = offset + 4; i <= sz - 4; i++) {
if (memcmp (buf + i, "\x7f\xff\x04\x00", 4) == 0) {
hdr_length = i - offset;
g_debug ("found END_ENTIRE at 0x%04x",
(guint) (i - offset));
break;
}
}
if (hdr_length == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"DP length invalid and no END_ENTIRE "
"found, possibly data truncation?");
return NULL;
}
if ((flags & FU_UEFI_DEVPATH_PARSE_FLAG_REPAIR) == 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"DP length invalid, reported 0x%04x, maybe 0x%04x",
hdr->length, hdr_length);
return NULL;
}
g_debug ("DP length invalid! Truncating from 0x%04x to 0x%04x",
hdr->length, hdr_length);
}
/* add new DP */
dp = g_new0 (FuUefiDevPath, 1);
dp->type = hdr->type;
dp->subtype = hdr->subtype;
if (hdr_length > 0)
dp->data = g_bytes_new (buf + offset + 4, hdr_length);
g_ptr_array_add (dps, dp);
/* advance to next DP */
offset += hdr_length;
if (offset + sizeof(efidp_header) > sz) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"DP length invalid after fixing");
return NULL;
}
}
return g_steal_pointer (&dps);
}