Coverage Report

Created: 2026-04-09 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-uswid-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2022 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#define G_LOG_DOMAIN "FuFirmware"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-bytes.h"
13
#include "fu-common.h"
14
#include "fu-coswid-firmware.h"
15
#include "fu-input-stream.h"
16
#include "fu-lzma-common.h"
17
#include "fu-partial-input-stream.h"
18
#include "fu-uswid-firmware.h"
19
#include "fu-uswid-struct.h"
20
21
/**
22
 * FuUswidFirmware:
23
 *
24
 * A uSWID header with multiple optionally-compressed SBOM sections.
25
 *
26
 * See also: [class@FuCoswidFirmware]
27
 */
28
29
typedef struct {
30
  guint8 hdrver;
31
  FuUswidPayloadCompression compression;
32
  FuUswidPayloadFormat format;
33
} FuUswidFirmwarePrivate;
34
35
105k
G_DEFINE_TYPE_WITH_PRIVATE(FuUswidFirmware, fu_uswid_firmware, FU_TYPE_FIRMWARE)
36
105k
#define GET_PRIVATE(o) (fu_uswid_firmware_get_instance_private(o))
37
38
97.3k
#define FU_USWID_FIRMARE_MINIMUM_HDRVER 1
39
40
static void
41
fu_uswid_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
42
0
{
43
0
  FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware);
44
0
  FuUswidFirmwarePrivate *priv = GET_PRIVATE(self);
45
0
  fu_xmlb_builder_insert_kx(bn, "hdrver", priv->hdrver);
46
0
  if (priv->compression != FU_USWID_PAYLOAD_COMPRESSION_NONE) {
47
0
    fu_xmlb_builder_insert_kv(
48
0
        bn,
49
0
        "compression",
50
0
        fu_uswid_payload_compression_to_string(priv->compression));
51
0
  }
52
0
  fu_xmlb_builder_insert_kv(bn, "format", fu_uswid_payload_format_to_string(priv->format));
53
0
}
54
55
static gboolean
56
fu_uswid_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error)
57
46.2k
{
58
46.2k
  return fu_struct_uswid_validate_stream(stream, offset, error);
59
46.2k
}
60
61
static GType
62
fu_uswid_firmware_format_to_gtype(FuUswidPayloadFormat format, GError **error)
63
18.4k
{
64
18.4k
  if (format == FU_USWID_PAYLOAD_FORMAT_COSWID)
65
14.1k
    return FU_TYPE_COSWID_FIRMWARE;
66
4.32k
  if (format == FU_USWID_PAYLOAD_FORMAT_CYCLONEDX)
67
1.01k
    return FU_TYPE_FIRMWARE;
68
3.31k
  if (format == FU_USWID_PAYLOAD_FORMAT_SPDX)
69
433
    return FU_TYPE_FIRMWARE;
70
2.87k
  g_set_error(error,
71
2.87k
        FWUPD_ERROR,
72
2.87k
        FWUPD_ERROR_NOT_SUPPORTED,
73
2.87k
        "format 0x%x is not supported",
74
2.87k
        format);
75
2.87k
  return G_TYPE_INVALID;
76
3.31k
}
77
78
static gboolean
79
fu_uswid_firmware_parse(FuFirmware *firmware,
80
      GInputStream *stream,
81
      FuFirmwareParseFlags flags,
82
      GError **error)
83
45.7k
{
84
45.7k
  FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware);
85
45.7k
  FuUswidFirmwarePrivate *priv = GET_PRIVATE(self);
86
45.7k
  GType img_gtype;
87
45.7k
  guint16 hdrsz;
88
45.7k
  guint32 payloadsz;
89
45.7k
  g_autoptr(FuStructUswid) st = NULL;
90
45.7k
  g_autoptr(GBytes) payload = NULL;
91
92
  /* unpack */
93
45.7k
  st = fu_struct_uswid_parse_stream(stream, 0x0, error);
94
45.7k
  if (st == NULL)
95
0
    return FALSE;
96
97
  /* hdrver */
98
45.7k
  priv->hdrver = fu_struct_uswid_get_hdrver(st);
99
45.7k
  if (priv->hdrver < FU_USWID_FIRMARE_MINIMUM_HDRVER) {
100
1.67k
    g_set_error_literal(error,
101
1.67k
            FWUPD_ERROR,
102
1.67k
            FWUPD_ERROR_NOT_SUPPORTED,
103
1.67k
            "header version was unsupported");
104
1.67k
    return FALSE;
105
1.67k
  }
106
107
  /* hdrsz+payloadsz */
108
44.0k
  hdrsz = fu_struct_uswid_get_hdrsz(st);
109
44.0k
  payloadsz = fu_struct_uswid_get_payloadsz(st);
110
44.0k
  if (payloadsz == 0x0) {
111
264
    g_set_error_literal(error,
112
264
            FWUPD_ERROR,
113
264
            FWUPD_ERROR_NOT_SUPPORTED,
114
264
            "payload size is invalid");
115
264
    return FALSE;
116
264
  }
117
43.8k
  fu_firmware_set_size(firmware, hdrsz + payloadsz);
118
119
  /* payload compression */
120
43.8k
  if (priv->hdrver >= 0x03) {
121
18.6k
    if (fu_struct_uswid_get_flags(st) & FU_USWID_HEADER_FLAG_COMPRESSED) {
122
3.71k
      priv->compression = fu_struct_uswid_get_compression(st);
123
14.9k
    } else {
124
14.9k
      priv->compression = FU_USWID_PAYLOAD_COMPRESSION_NONE;
125
14.9k
    }
126
25.1k
  } else if (priv->hdrver >= 0x02) {
127
18.7k
    priv->compression = fu_struct_uswid_get_flags(st) & FU_USWID_HEADER_FLAG_COMPRESSED
128
18.7k
          ? FU_USWID_PAYLOAD_COMPRESSION_ZLIB
129
18.7k
          : FU_USWID_PAYLOAD_COMPRESSION_NONE;
130
18.7k
  } else {
131
6.41k
    priv->compression = FU_USWID_PAYLOAD_COMPRESSION_NONE;
132
6.41k
  }
133
134
  /* payload format */
135
43.8k
  if (priv->hdrver >= 0x04) {
136
18.4k
    priv->format = fu_struct_uswid_get_format(st);
137
18.4k
    img_gtype = fu_uswid_firmware_format_to_gtype(priv->format, error);
138
18.4k
    if (img_gtype == G_TYPE_INVALID)
139
2.87k
      return FALSE;
140
25.3k
  } else {
141
25.3k
    priv->format = FU_USWID_PAYLOAD_FORMAT_COSWID;
142
25.3k
    img_gtype = FU_TYPE_COSWID_FIRMWARE;
143
25.3k
  }
144
145
  /* zlib stream */
146
40.9k
  if (priv->compression == FU_USWID_PAYLOAD_COMPRESSION_ZLIB) {
147
4.72k
    g_autoptr(GConverter) conv = NULL;
148
4.72k
    g_autoptr(GInputStream) istream1 = NULL;
149
4.72k
    g_autoptr(GInputStream) istream2 = NULL;
150
4.72k
    conv = G_CONVERTER(g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_ZLIB));
151
4.72k
    istream1 = fu_partial_input_stream_new(stream, hdrsz, payloadsz, error);
152
4.72k
    if (istream1 == NULL) {
153
1.27k
      g_prefix_error_literal(error, "failed to cut uSWID payload: ");
154
1.27k
      return FALSE;
155
1.27k
    }
156
3.45k
    if (!g_seekable_seek(G_SEEKABLE(istream1), 0, G_SEEK_SET, NULL, error))
157
0
      return FALSE;
158
3.45k
    istream2 = g_converter_input_stream_new(istream1, conv);
159
3.45k
    g_filter_input_stream_set_close_base_stream(G_FILTER_INPUT_STREAM(istream2), FALSE);
160
3.45k
    payload = fu_input_stream_read_bytes(istream2, 0, G_MAXSIZE, NULL, error);
161
3.45k
    if (payload == NULL)
162
3.45k
      return FALSE;
163
36.2k
  } else if (priv->compression == FU_USWID_PAYLOAD_COMPRESSION_LZMA) {
164
909
    g_autoptr(GBytes) payload_tmp = NULL;
165
909
    payload_tmp = fu_input_stream_read_bytes(stream, hdrsz, payloadsz, NULL, error);
166
909
    if (payload_tmp == NULL)
167
389
      return FALSE;
168
520
    payload = fu_lzma_decompress_bytes(payload_tmp, 16 * 1024 * 1024, error);
169
520
    if (payload == NULL)
170
377
      return FALSE;
171
35.3k
  } else if (priv->compression == FU_USWID_PAYLOAD_COMPRESSION_NONE) {
172
34.5k
    payload = fu_input_stream_read_bytes(stream, hdrsz, payloadsz, NULL, error);
173
34.5k
    if (payload == NULL)
174
1.15k
      return FALSE;
175
34.5k
  } else {
176
742
    g_set_error(error,
177
742
          FWUPD_ERROR,
178
742
          FWUPD_ERROR_NOT_SUPPORTED,
179
742
          "compression format 0x%x is not supported",
180
742
          priv->compression);
181
742
    return FALSE;
182
742
  }
183
184
  /* payload */
185
33.5k
  payloadsz = g_bytes_get_size(payload);
186
69.9k
  for (gsize offset_tmp = 0; offset_tmp < payloadsz;) {
187
68.8k
    g_autoptr(FuFirmware) img = g_object_new(img_gtype, NULL);
188
68.8k
    g_autoptr(GBytes) img_blob = NULL;
189
190
    /* parse SBOM component */
191
68.8k
    img_blob = fu_bytes_new_offset(payload, offset_tmp, payloadsz - offset_tmp, error);
192
68.8k
    if (img_blob == NULL)
193
0
      return FALSE;
194
68.8k
    if (!fu_firmware_parse_bytes(img,
195
68.8k
               img_blob,
196
68.8k
               0x0,
197
68.8k
               flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
198
68.8k
               error))
199
31.9k
      return FALSE;
200
36.8k
    if (!fu_firmware_add_image(firmware, img, error))
201
1
      return FALSE;
202
36.8k
    if (fu_firmware_get_size(img) == 0) {
203
428
      g_set_error_literal(error,
204
428
              FWUPD_ERROR,
205
428
              FWUPD_ERROR_NOT_SUPPORTED,
206
428
              "read no bytes from uSWID child");
207
428
      return FALSE;
208
428
    }
209
36.4k
    offset_tmp += fu_firmware_get_size(img);
210
36.4k
  }
211
212
  /* success */
213
1.13k
  return TRUE;
214
33.5k
}
215
216
static GByteArray *
217
fu_uswid_firmware_write(FuFirmware *firmware, GError **error)
218
775
{
219
775
  FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware);
220
775
  FuUswidFirmwarePrivate *priv = GET_PRIVATE(self);
221
775
  FuUswidHeaderFlags flags = FU_USWID_HEADER_FLAG_NONE;
222
775
  g_autoptr(FuStructUswid) st = fu_struct_uswid_new();
223
775
  g_autoptr(GByteArray) payload = g_byte_array_new();
224
775
  g_autoptr(GBytes) payload_blob = NULL;
225
775
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
226
227
  /* sanity check */
228
775
  if (priv->hdrver > FU_STRUCT_USWID_DEFAULT_HDRVER) {
229
33
    g_set_error(error,
230
33
          FWUPD_ERROR,
231
33
          FWUPD_ERROR_NOT_SUPPORTED,
232
33
          "no idea how to write header format 0x%02x",
233
33
          priv->hdrver);
234
33
    return NULL;
235
33
  }
236
237
  /* generate early so we know the size */
238
9.37k
  for (guint i = 0; i < images->len; i++) {
239
8.63k
    FuFirmware *img = g_ptr_array_index(images, i);
240
8.63k
    g_autoptr(GBytes) fw = fu_firmware_write(img, error);
241
8.63k
    if (fw == NULL)
242
0
      return NULL;
243
8.63k
    fu_byte_array_append_bytes(payload, fw);
244
8.63k
  }
245
246
  /* compression flag */
247
742
  if (priv->compression != FU_USWID_PAYLOAD_COMPRESSION_NONE)
248
13
    flags |= FU_USWID_HEADER_FLAG_COMPRESSED;
249
250
  /* compression format */
251
742
  if (priv->compression == FU_USWID_PAYLOAD_COMPRESSION_ZLIB) {
252
2
    g_autoptr(GConverter) conv = NULL;
253
2
    g_autoptr(GInputStream) istream1 = NULL;
254
2
    g_autoptr(GInputStream) istream2 = NULL;
255
256
2
    conv = G_CONVERTER(g_zlib_compressor_new(G_ZLIB_COMPRESSOR_FORMAT_ZLIB, -1));
257
2
    istream1 = g_memory_input_stream_new_from_data(payload->data, payload->len, NULL);
258
2
    istream2 = g_converter_input_stream_new(istream1, conv);
259
2
    payload_blob = fu_input_stream_read_bytes(istream2, 0, G_MAXSIZE, NULL, error);
260
2
    if (payload_blob == NULL)
261
0
      return NULL;
262
740
  } else if (priv->compression == FU_USWID_PAYLOAD_COMPRESSION_LZMA) {
263
11
    g_autoptr(GBytes) payload_tmp = g_bytes_new(payload->data, payload->len);
264
11
    payload_blob = fu_lzma_compress_bytes(payload_tmp, error);
265
11
    if (payload_blob == NULL)
266
0
      return NULL;
267
729
  } else {
268
729
    payload_blob = g_bytes_new(payload->data, payload->len);
269
729
  }
270
271
  /* pack */
272
742
  fu_struct_uswid_set_hdrver(st, priv->hdrver);
273
742
  fu_struct_uswid_set_payloadsz(st, g_bytes_get_size(payload_blob));
274
742
  fu_struct_uswid_set_flags(st, flags);
275
742
  fu_struct_uswid_set_compression(st, priv->compression);
276
742
  fu_struct_uswid_set_format(st, priv->format);
277
278
  /* previous headers were smaller in size */
279
742
  if (priv->hdrver == 3) {
280
35
    g_byte_array_set_size(st->buf, st->buf->len - 1);
281
707
  } else if (priv->hdrver == 2) {
282
336
    if (priv->compression != FU_USWID_PAYLOAD_COMPRESSION_NONE &&
283
2
        priv->compression != FU_USWID_PAYLOAD_COMPRESSION_ZLIB) {
284
0
      g_set_error_literal(error,
285
0
              FWUPD_ERROR,
286
0
              FWUPD_ERROR_NOT_SUPPORTED,
287
0
              "hdrver 0x02 only supports zlib compression");
288
0
      return NULL;
289
0
    }
290
336
    g_byte_array_set_size(st->buf, st->buf->len - 2);
291
371
  } else if (priv->hdrver == 1) {
292
368
    g_byte_array_set_size(st->buf, st->buf->len - 3);
293
368
  }
294
742
  fu_struct_uswid_set_hdrsz(st, st->buf->len);
295
296
  /* success */
297
742
  fu_byte_array_append_bytes(st->buf, payload_blob);
298
742
  return g_steal_pointer(&st->buf);
299
742
}
300
301
static gboolean
302
fu_uswid_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
303
0
{
304
0
  FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware);
305
0
  FuUswidFirmwarePrivate *priv = GET_PRIVATE(self);
306
0
  const gchar *str;
307
0
  guint64 tmp;
308
309
  /* simple properties */
310
0
  tmp = xb_node_query_text_as_uint(n, "hdrver", NULL);
311
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8)
312
0
    priv->hdrver = tmp;
313
314
  /* simple properties */
315
0
  str = xb_node_query_text(n, "compression", NULL);
316
0
  if (str != NULL) {
317
0
    priv->compression = fu_uswid_payload_compression_from_string(str);
318
0
    if (priv->compression == FU_USWID_PAYLOAD_COMPRESSION_NONE) {
319
0
      g_set_error(error,
320
0
            FWUPD_ERROR,
321
0
            FWUPD_ERROR_INVALID_DATA,
322
0
            "invalid compression type %s",
323
0
            str);
324
0
      return FALSE;
325
0
    }
326
0
  } else {
327
0
    priv->compression = FU_USWID_PAYLOAD_COMPRESSION_NONE;
328
0
  }
329
330
  /* success */
331
0
  return TRUE;
332
0
}
333
334
static void
335
fu_uswid_firmware_add_magic(FuFirmware *firmware)
336
51.6k
{
337
51.6k
  fu_firmware_add_magic(firmware,
338
51.6k
            (const guint8 *)FU_STRUCT_USWID_DEFAULT_MAGIC,
339
51.6k
            sizeof(fwupd_guid_t),
340
51.6k
            0x0);
341
51.6k
}
342
343
static void
344
fu_uswid_firmware_init(FuUswidFirmware *self)
345
51.6k
{
346
51.6k
  FuUswidFirmwarePrivate *priv = GET_PRIVATE(self);
347
51.6k
  priv->hdrver = FU_USWID_FIRMARE_MINIMUM_HDRVER;
348
51.6k
  priv->compression = FU_USWID_PAYLOAD_COMPRESSION_NONE;
349
51.6k
  priv->format = FU_USWID_PAYLOAD_FORMAT_COSWID;
350
51.6k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
351
51.6k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALWAYS_SEARCH);
352
51.6k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 2000);
353
51.6k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_COSWID_FIRMWARE);
354
51.6k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE);
355
51.6k
}
356
357
static void
358
fu_uswid_firmware_class_init(FuUswidFirmwareClass *klass)
359
3
{
360
3
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
361
3
  firmware_class->validate = fu_uswid_firmware_validate;
362
3
  firmware_class->parse = fu_uswid_firmware_parse;
363
3
  firmware_class->write = fu_uswid_firmware_write;
364
3
  firmware_class->build = fu_uswid_firmware_build;
365
3
  firmware_class->export = fu_uswid_firmware_export;
366
3
  firmware_class->add_magic = fu_uswid_firmware_add_magic;
367
3
}
368
369
/**
370
 * fu_uswid_firmware_new:
371
 *
372
 * Creates a new #FuFirmware of sub type uSWID
373
 *
374
 * Since: 1.8.0
375
 **/
376
FuFirmware *
377
fu_uswid_firmware_new(void)
378
0
{
379
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_USWID_FIRMWARE, NULL));
380
0
}