mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-14 15:33:09 +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
187 lines
4.5 KiB
C
187 lines
4.5 KiB
C
/*
|
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-common.h"
|
|
#include "fu-uefi-devpath.h"
|
|
#include "fu-uefi-update-info.h"
|
|
#include "fu-uefi-common.h"
|
|
#include "fu-ucs2.h"
|
|
|
|
#include "fwupd-error.h"
|
|
|
|
struct _FuUefiUpdateInfo {
|
|
GObject parent_instance;
|
|
guint32 version;
|
|
gchar *guid;
|
|
gchar *capsule_fn;
|
|
guint32 capsule_flags;
|
|
guint64 hw_inst;
|
|
FuUefiUpdateInfoStatus status;
|
|
};
|
|
|
|
G_DEFINE_TYPE (FuUefiUpdateInfo, fu_uefi_update_info, G_TYPE_OBJECT)
|
|
|
|
const gchar *
|
|
fu_uefi_update_info_status_to_string (FuUefiUpdateInfoStatus status)
|
|
{
|
|
if (status == FU_UEFI_UPDATE_INFO_STATUS_ATTEMPT_UPDATE)
|
|
return "attempt-update";
|
|
if (status == FU_UEFI_UPDATE_INFO_STATUS_ATTEMPTED)
|
|
return "attempted";
|
|
return "unknown";
|
|
}
|
|
|
|
static gchar *
|
|
fu_uefi_update_info_parse_dp (const guint8 *buf, gsize sz, GError **error)
|
|
{
|
|
GBytes *dp_data;
|
|
const gchar *data;
|
|
gsize ucs2sz = 0;
|
|
g_autofree gchar *relpath = NULL;
|
|
g_autofree guint16 *ucs2file = NULL;
|
|
g_autoptr(GPtrArray) dps = NULL;
|
|
|
|
g_return_val_if_fail (buf != NULL, NULL);
|
|
g_return_val_if_fail (sz != 0, NULL);
|
|
|
|
/* get all headers */
|
|
dps = fu_uefi_devpath_parse (buf, sz, FU_UEFI_DEVPATH_PARSE_FLAG_REPAIR, error);
|
|
if (dps == NULL)
|
|
return NULL;
|
|
dp_data = fu_uefi_devpath_find_data (dps,
|
|
EFIDP_MEDIA_TYPE,
|
|
EFIDP_MEDIA_FILE,
|
|
error);
|
|
if (dp_data == NULL)
|
|
return NULL;
|
|
|
|
/* convert to UTF-8 */
|
|
data = g_bytes_get_data (dp_data, &ucs2sz);
|
|
ucs2file = g_new0 (guint16, (ucs2sz / 2) + 1);
|
|
memcpy (ucs2file, data, ucs2sz);
|
|
relpath = fu_ucs2_to_uft8 (ucs2file, ucs2sz / sizeof (guint16));
|
|
if (relpath == NULL) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"cannot convert to UTF-8");
|
|
return NULL;
|
|
}
|
|
g_strdelimit (relpath, "\\", '/');
|
|
return g_steal_pointer (&relpath);
|
|
}
|
|
|
|
gboolean
|
|
fu_uefi_update_info_parse (FuUefiUpdateInfo *self, const guint8 *buf, gsize sz, GError **error)
|
|
{
|
|
efi_update_info_t info;
|
|
efi_guid_t guid_tmp;
|
|
|
|
g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), FALSE);
|
|
|
|
if (sz < sizeof(efi_update_info_t)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"EFI variable is corrupt");
|
|
return FALSE;
|
|
}
|
|
memcpy (&info, buf, sizeof(info));
|
|
self->version = info.update_info_version;
|
|
self->capsule_flags = info.capsule_flags;
|
|
self->hw_inst = info.hw_inst;
|
|
self->status = info.status;
|
|
memcpy (&guid_tmp, &info.guid, sizeof(efi_guid_t));
|
|
if (efi_guid_to_str (&guid_tmp, &self->guid) < 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"failed to convert GUID");
|
|
return FALSE;
|
|
}
|
|
if (sz > sizeof(efi_update_info_t)) {
|
|
self->capsule_fn = fu_uefi_update_info_parse_dp (buf + sizeof(efi_update_info_t),
|
|
sz - sizeof(efi_update_info_t),
|
|
error);
|
|
if (self->capsule_fn == NULL)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
const gchar *
|
|
fu_uefi_update_info_get_guid (FuUefiUpdateInfo *self)
|
|
{
|
|
g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), NULL);
|
|
return self->guid;
|
|
}
|
|
|
|
const gchar *
|
|
fu_uefi_update_info_get_capsule_fn (FuUefiUpdateInfo *self)
|
|
{
|
|
g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), NULL);
|
|
return self->capsule_fn;
|
|
}
|
|
|
|
guint32
|
|
fu_uefi_update_info_get_version (FuUefiUpdateInfo *self)
|
|
{
|
|
g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0);
|
|
return self->version;
|
|
}
|
|
|
|
guint32
|
|
fu_uefi_update_info_get_capsule_flags (FuUefiUpdateInfo *self)
|
|
{
|
|
g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0);
|
|
return self->capsule_flags;
|
|
}
|
|
|
|
guint64
|
|
fu_uefi_update_info_get_hw_inst (FuUefiUpdateInfo *self)
|
|
{
|
|
g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0);
|
|
return self->hw_inst;
|
|
}
|
|
|
|
FuUefiUpdateInfoStatus
|
|
fu_uefi_update_info_get_status (FuUefiUpdateInfo *self)
|
|
{
|
|
g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0);
|
|
return self->status;
|
|
}
|
|
|
|
static void
|
|
fu_uefi_update_info_finalize (GObject *object)
|
|
{
|
|
FuUefiUpdateInfo *self = FU_UEFI_UPDATE_INFO (object);
|
|
g_free (self->guid);
|
|
g_free (self->capsule_fn);
|
|
G_OBJECT_CLASS (fu_uefi_update_info_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
fu_uefi_update_info_class_init (FuUefiUpdateInfoClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
object_class->finalize = fu_uefi_update_info_finalize;
|
|
}
|
|
|
|
static void
|
|
fu_uefi_update_info_init (FuUefiUpdateInfo *self)
|
|
{
|
|
}
|
|
|
|
FuUefiUpdateInfo *
|
|
fu_uefi_update_info_new (void)
|
|
{
|
|
FuUefiUpdateInfo *self;
|
|
self = g_object_new (FU_TYPE_UEFI_UPDATE_INFO, NULL);
|
|
return FU_UEFI_UPDATE_INFO (self);
|
|
}
|