Coverage Report

Created: 2025-08-29 06:48

/src/fwupd/libfwupdplugin/fu-efi-file.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2020 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-byte-array.h"
10
#include "fu-common.h"
11
#include "fu-efi-common.h"
12
#include "fu-efi-file.h"
13
#include "fu-efi-section.h"
14
#include "fu-efi-struct.h"
15
#include "fu-input-stream.h"
16
#include "fu-partial-input-stream.h"
17
#include "fu-sum.h"
18
19
/**
20
 * FuEfiFile:
21
 *
22
 * A UEFI file.
23
 *
24
 * See also: [class@FuFirmware]
25
 */
26
27
typedef struct {
28
  guint8 type;
29
  guint8 attrib;
30
} FuEfiFilePrivate;
31
32
G_DEFINE_TYPE_WITH_PRIVATE(FuEfiFile, fu_efi_file, FU_TYPE_FIRMWARE)
33
68.7k
#define GET_PRIVATE(o) (fu_efi_file_get_instance_private(o))
34
35
7.53k
#define FU_EFI_FILE_SIZE_MAX 0x1000000 /* 16 MB */
36
37
static void
38
fu_efi_file_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
39
0
{
40
0
  FuEfiFile *self = FU_EFI_FILE(firmware);
41
0
  FuEfiFilePrivate *priv = GET_PRIVATE(self);
42
43
0
  fu_xmlb_builder_insert_kx(bn, "attrib", priv->attrib);
44
0
  fu_xmlb_builder_insert_kx(bn, "type", priv->type);
45
0
  if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) {
46
0
    fu_xmlb_builder_insert_kv(bn,
47
0
            "name",
48
0
            fu_efi_guid_to_name(fu_firmware_get_id(firmware)));
49
0
    fu_xmlb_builder_insert_kv(bn, "type_name", fu_efi_file_type_to_string(priv->type));
50
0
  }
51
0
}
52
53
static guint8
54
fu_efi_file_hdr_checksum8(GBytes *blob)
55
36.9k
{
56
36.9k
  gsize bufsz = 0;
57
36.9k
  guint8 checksum = 0;
58
36.9k
  const guint8 *buf = g_bytes_get_data(blob, &bufsz);
59
926k
  for (gsize i = 0; i < bufsz; i++) {
60
889k
    if (i == FU_STRUCT_EFI_FILE_OFFSET_HDR_CHECKSUM)
61
36.9k
      continue;
62
852k
    if (i == FU_STRUCT_EFI_FILE_OFFSET_DATA_CHECKSUM)
63
36.9k
      continue;
64
815k
    if (i == FU_STRUCT_EFI_FILE_OFFSET_STATE)
65
36.9k
      continue;
66
778k
    checksum += buf[i];
67
778k
  }
68
36.9k
  return (guint8)(0x100u - (guint)checksum);
69
36.9k
}
70
71
static gboolean
72
fu_efi_file_parse(FuFirmware *firmware,
73
      GInputStream *stream,
74
      FuFirmwareParseFlags flags,
75
      GError **error)
76
30.5k
{
77
30.5k
  FuEfiFile *self = FU_EFI_FILE(firmware);
78
30.5k
  FuEfiFilePrivate *priv = GET_PRIVATE(self);
79
30.5k
  guint32 size = 0x0;
80
30.5k
  g_autofree gchar *guid_str = NULL;
81
30.5k
  g_autoptr(GByteArray) st = NULL;
82
30.5k
  g_autoptr(GInputStream) partial_stream = NULL;
83
84
  /* parse */
85
30.5k
  st = fu_struct_efi_file_parse_stream(stream, 0x0, error);
86
30.5k
  if (st == NULL)
87
88
    return FALSE;
88
30.4k
  priv->type = fu_struct_efi_file_get_type(st);
89
30.4k
  priv->attrib = fu_struct_efi_file_get_attrs(st);
90
30.4k
  guid_str =
91
30.4k
      fwupd_guid_to_string(fu_struct_efi_file_get_name(st), FWUPD_GUID_FLAG_MIXED_ENDIAN);
92
30.4k
  fu_firmware_set_id(firmware, guid_str);
93
94
  /* extended size exists so size must be set to zero */
95
30.4k
  if (priv->attrib & FU_EFI_FILE_ATTRIB_LARGE_FILE) {
96
337
    if (fu_struct_efi_file_get_size(st) != 0) {
97
99
      g_set_error(error,
98
99
            FWUPD_ERROR,
99
99
            FWUPD_ERROR_INTERNAL,
100
99
            "invalid FFS size -- expected 0x0 and got 0x%x",
101
99
            (guint)fu_struct_efi_file_get_size(st));
102
99
      return FALSE;
103
99
    }
104
238
    fu_struct_efi_file_unref(st);
105
238
    st = fu_struct_efi_file2_parse_stream(stream, 0x0, error);
106
238
    if (st == NULL)
107
9
      return FALSE;
108
229
    size = fu_struct_efi_file2_get_extended_size(st);
109
30.1k
  } else {
110
30.1k
    size = fu_struct_efi_file_get_size(st);
111
30.1k
  }
112
30.3k
  if (size < st->len) {
113
25
    g_set_error(error,
114
25
          FWUPD_ERROR,
115
25
          FWUPD_ERROR_INTERNAL,
116
25
          "invalid FFS length, got 0x%x",
117
25
          (guint)size);
118
25
    return FALSE;
119
25
  }
120
121
  /* verify header checksum */
122
30.3k
  if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) {
123
30.3k
    guint8 hdr_checksum_verify;
124
30.3k
    g_autoptr(GBytes) hdr_blob = NULL;
125
126
30.3k
    hdr_blob = fu_input_stream_read_bytes(stream, 0x0, st->len, NULL, error);
127
30.3k
    if (hdr_blob == NULL)
128
0
      return FALSE;
129
30.3k
    hdr_checksum_verify = fu_efi_file_hdr_checksum8(hdr_blob);
130
30.3k
    if (hdr_checksum_verify != fu_struct_efi_file_get_hdr_checksum(st)) {
131
240
      g_set_error(error,
132
240
            FWUPD_ERROR,
133
240
            FWUPD_ERROR_INVALID_FILE,
134
240
            "checksum invalid, got %02x, expected %02x",
135
240
            hdr_checksum_verify,
136
240
            fu_struct_efi_file_get_hdr_checksum(st));
137
240
      return FALSE;
138
240
    }
139
30.3k
  }
140
141
  /* add simple blob */
142
30.1k
  partial_stream = fu_partial_input_stream_new(stream, st->len, size - st->len, error);
143
30.1k
  if (partial_stream == NULL) {
144
68
    g_prefix_error_literal(error, "failed to cut EFI blob: ");
145
68
    return FALSE;
146
68
  }
147
148
  /* verify data checksum */
149
30.0k
  if ((priv->attrib & FU_EFI_FILE_ATTRIB_CHECKSUM) > 0 &&
150
30.0k
      (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) {
151
686
    guint8 data_checksum_verify = 0;
152
686
    if (!fu_input_stream_compute_sum8(partial_stream, &data_checksum_verify, error))
153
0
      return FALSE;
154
686
    if (0x100 - data_checksum_verify != fu_struct_efi_file_get_data_checksum(st)) {
155
72
      g_set_error(error,
156
72
            FWUPD_ERROR,
157
72
            FWUPD_ERROR_INVALID_FILE,
158
72
            "checksum invalid, got 0x%02x, expected 0x%02x",
159
72
            0x100u - data_checksum_verify,
160
72
            fu_struct_efi_file_get_data_checksum(st));
161
72
      return FALSE;
162
72
    }
163
686
  }
164
165
  /* add sections */
166
29.9k
  if (priv->type != FU_EFI_FILE_TYPE_FFS_PAD && priv->type != FU_EFI_FILE_TYPE_RAW) {
167
18.9k
    if (!fu_efi_parse_sections(firmware, partial_stream, 0, flags, error)) {
168
3.68k
      g_prefix_error_literal(error, "failed to add firmware image: ");
169
3.68k
      return FALSE;
170
3.68k
    }
171
18.9k
  } else {
172
11.0k
    if (!fu_firmware_set_stream(firmware, partial_stream, error))
173
0
      return FALSE;
174
11.0k
  }
175
176
  /* align size for volume */
177
26.2k
  fu_firmware_set_size(firmware,
178
26.2k
           fu_common_align_up(size, fu_firmware_get_alignment(firmware)));
179
180
  /* success */
181
26.2k
  return TRUE;
182
29.9k
}
183
184
static GBytes *
185
fu_efi_file_write_sections(FuFirmware *firmware, GError **error)
186
7.03k
{
187
7.03k
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
188
7.03k
  g_autoptr(GByteArray) buf = g_byte_array_new();
189
190
  /* sanity check */
191
7.03k
  if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) {
192
0
    g_set_error(error,
193
0
          FWUPD_ERROR,
194
0
          FWUPD_ERROR_INVALID_FILE,
195
0
          "alignment invalid, got 0x%02x",
196
0
          fu_firmware_get_alignment(firmware));
197
0
    return NULL;
198
0
  }
199
200
  /* no sections defined */
201
7.03k
  if (images->len == 0)
202
2.81k
    return fu_firmware_get_bytes_with_patches(firmware, error);
203
204
  /* add each section */
205
11.7k
  for (guint i = 0; i < images->len; i++) {
206
7.91k
    FuFirmware *img = g_ptr_array_index(images, i);
207
7.91k
    g_autoptr(GBytes) blob = NULL;
208
7.91k
    fu_firmware_set_offset(img, buf->len);
209
7.91k
    blob = fu_firmware_write(img, error);
210
7.91k
    if (blob == NULL)
211
383
      return NULL;
212
7.53k
    fu_byte_array_append_bytes(buf, blob);
213
7.53k
    fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_4, 0xFF);
214
215
    /* sanity check */
216
7.53k
    if (buf->len > FU_EFI_FILE_SIZE_MAX) {
217
0
      g_set_error(error,
218
0
            FWUPD_ERROR,
219
0
            FWUPD_ERROR_INVALID_FILE,
220
0
            "EFI file too large, 0x%02x > 0x%02x",
221
0
            (guint)buf->len,
222
0
            (guint)FU_EFI_FILE_SIZE_MAX);
223
0
      return NULL;
224
0
    }
225
7.53k
  }
226
227
  /* success */
228
3.83k
  return g_bytes_new(buf->data, buf->len);
229
4.21k
}
230
231
static GByteArray *
232
fu_efi_file_write(FuFirmware *firmware, GError **error)
233
7.03k
{
234
7.03k
  FuEfiFile *self = FU_EFI_FILE(firmware);
235
7.03k
  FuEfiFilePrivate *priv = GET_PRIVATE(self);
236
7.03k
  fwupd_guid_t guid = {0x0};
237
7.03k
  g_autoptr(GByteArray) st = fu_struct_efi_file_new();
238
7.03k
  g_autoptr(GBytes) blob = NULL;
239
7.03k
  g_autoptr(GBytes) hdr_blob = NULL;
240
241
  /* simple blob for now */
242
7.03k
  blob = fu_efi_file_write_sections(firmware, error);
243
7.03k
  if (blob == NULL)
244
421
    return NULL;
245
6.61k
  if (fu_firmware_get_id(firmware) != NULL) {
246
6.61k
    if (!fwupd_guid_from_string(fu_firmware_get_id(firmware),
247
6.61k
              &guid,
248
6.61k
              FWUPD_GUID_FLAG_MIXED_ENDIAN,
249
6.61k
              error))
250
0
      return NULL;
251
6.61k
  }
252
6.61k
  fu_struct_efi_file_set_name(st, &guid);
253
6.61k
  fu_struct_efi_file_set_hdr_checksum(st, 0x0);
254
6.61k
  fu_struct_efi_file_set_data_checksum(st, 0x100 - fu_sum8_bytes(blob));
255
6.61k
  fu_struct_efi_file_set_type(st, priv->type);
256
6.61k
  fu_struct_efi_file_set_attrs(st, priv->attrib);
257
6.61k
  fu_struct_efi_file_set_size(st, g_bytes_get_size(blob) + st->len);
258
259
  /* fix up header checksum */
260
6.61k
  hdr_blob = g_bytes_new_static(st->data, st->len);
261
6.61k
  fu_struct_efi_file_set_hdr_checksum(st, fu_efi_file_hdr_checksum8(hdr_blob));
262
263
  /* success */
264
6.61k
  fu_byte_array_append_bytes(st, blob);
265
6.61k
  return g_steal_pointer(&st);
266
6.61k
}
267
268
static gboolean
269
fu_efi_file_build(FuFirmware *firmware, XbNode *n, GError **error)
270
0
{
271
0
  FuEfiFile *self = FU_EFI_FILE(firmware);
272
0
  FuEfiFilePrivate *priv = GET_PRIVATE(self);
273
0
  guint64 tmp;
274
275
  /* simple properties */
276
0
  tmp = xb_node_query_text_as_uint(n, "type", NULL);
277
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8)
278
0
    priv->type = tmp;
279
0
  tmp = xb_node_query_text_as_uint(n, "attrib", NULL);
280
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8)
281
0
    priv->attrib = tmp;
282
283
  /* success */
284
0
  return TRUE;
285
0
}
286
287
static void
288
fu_efi_file_init(FuEfiFile *self)
289
31.1k
{
290
31.1k
  FuEfiFilePrivate *priv = GET_PRIVATE(self);
291
31.1k
  priv->attrib = FU_EFI_FILE_ATTRIB_NONE;
292
31.1k
  priv->type = FU_EFI_FILE_TYPE_RAW;
293
31.1k
  fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_8);
294
31.1k
  g_type_ensure(FU_TYPE_EFI_SECTION);
295
31.1k
}
296
297
static void
298
fu_efi_file_class_init(FuEfiFileClass *klass)
299
2
{
300
2
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
301
2
  firmware_class->parse = fu_efi_file_parse;
302
2
  firmware_class->write = fu_efi_file_write;
303
2
  firmware_class->build = fu_efi_file_build;
304
2
  firmware_class->export = fu_efi_file_export;
305
2
}
306
307
/**
308
 * fu_efi_file_new:
309
 *
310
 * Creates a new #FuFirmware
311
 *
312
 * Since: 2.0.0
313
 **/
314
FuFirmware *
315
fu_efi_file_new(void)
316
31.1k
{
317
31.1k
  return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FILE, NULL));
318
31.1k
}