mirror of
				https://git.proxmox.com/git/fwupd
				synced 2025-11-04 10:14:22 +00:00 
			
		
		
		
	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
		
	
			
		
			
				
	
	
		
			129 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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);
 | 
						|
}
 |