Coverage Report

Created: 2026-01-25 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-fit-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
39
#define G_LOG_DOMAIN "FuFirmware"
8
9
#include "config.h"
10
11
#include "fu-bytes.h"
12
#include "fu-crc.h"
13
#include "fu-dump.h"
14
#include "fu-fdt-image.h"
15
#include "fu-fit-firmware.h"
16
#include "fu-input-stream.h"
17
#include "fu-mem.h"
18
19
/**
20
 * FuFitFirmware:
21
 *
22
 * A Flat Image Tree.
23
 *
24
 * Documented:
25
 * https://github.com/u-boot/u-boot/blob/master/doc/uImage.FIT/source_file_format.txt
26
 *
27
 * See also: [class@FuFdtFirmware]
28
 */
29
30
3.92k
G_DEFINE_TYPE(FuFitFirmware, fu_fit_firmware, FU_TYPE_FDT_FIRMWARE)
31
3.92k
32
3.92k
static FuFdtImage *
33
3.92k
fu_fit_firmware_get_image_root(FuFitFirmware *self)
34
3.92k
{
35
0
  FuFirmware *img = fu_firmware_get_image_by_id(FU_FIRMWARE(self), NULL, NULL);
36
0
  if (img != NULL)
37
0
    return FU_FDT_IMAGE(img);
38
0
  img = fu_fdt_image_new();
39
0
  fu_fdt_image_set_attr_uint32(FU_FDT_IMAGE(img), FU_FIT_FIRMWARE_ATTR_TIMESTAMP, 0x0);
40
0
  fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "description", "Firmware image");
41
0
  fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "creator", "fwupd");
42
0
  if (!fu_firmware_add_image(FU_FIRMWARE(self), img, NULL))
43
0
    return NULL;
44
0
  return FU_FDT_IMAGE(img);
45
0
}
46
47
/**
48
 * fu_fit_firmware_get_timestamp:
49
 * @self: a #FuFitFirmware
50
 *
51
 * Gets the creation timestamp.
52
 *
53
 * Returns: integer
54
 *
55
 * Since: 1.8.2
56
 **/
57
guint32
58
fu_fit_firmware_get_timestamp(FuFitFirmware *self)
59
0
{
60
0
  guint32 tmp = 0;
61
0
  g_autoptr(FuFdtImage) img_root = fu_fit_firmware_get_image_root(self);
62
63
0
  g_return_val_if_fail(FU_IS_FIT_FIRMWARE(self), 0x0);
64
65
0
  if (img_root == NULL)
66
0
    return 0;
67
  /* this has to exist */
68
0
  (void)fu_fdt_image_get_attr_u32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, &tmp, NULL);
69
0
  return tmp;
70
0
}
71
72
/**
73
 * fu_fit_firmware_set_timestamp:
74
 * @self: a #FuFitFirmware
75
 * @timestamp: integer value
76
 *
77
 * Sets the creation timestamp.
78
 *
79
 * Since: 1.8.2
80
 **/
81
void
82
fu_fit_firmware_set_timestamp(FuFitFirmware *self, guint32 timestamp)
83
0
{
84
0
  g_autoptr(FuFdtImage) img_root = fu_fit_firmware_get_image_root(self);
85
0
  g_return_if_fail(FU_IS_FIT_FIRMWARE(self));
86
0
  if (img_root == NULL)
87
0
    return;
88
0
  fu_fdt_image_set_attr_uint32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, timestamp);
89
0
}
90
91
static gboolean
92
fu_fit_firmware_verify_crc32(FuFitFirmware *self,
93
           FuFirmware *img,
94
           FuFirmware *img_hash,
95
           GBytes *blob,
96
           GError **error)
97
0
{
98
0
  guint32 value = 0;
99
0
  guint32 value_calc;
100
101
  /* get value and verify */
102
0
  if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img_hash),
103
0
               FU_FIT_FIRMWARE_ATTR_VALUE,
104
0
               &value,
105
0
               error))
106
0
    return FALSE;
107
0
  value_calc = fu_crc32_bytes(FU_CRC_KIND_B32_STANDARD, blob);
108
0
  if (value_calc != value) {
109
0
    g_set_error(error,
110
0
          FWUPD_ERROR,
111
0
          FWUPD_ERROR_INVALID_DATA,
112
0
          "%s CRC did not match, got 0x%x, expected 0x%x",
113
0
          fu_firmware_get_id(img),
114
0
          value,
115
0
          value_calc);
116
0
    return FALSE;
117
0
  }
118
119
  /* success */
120
0
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
121
0
  return TRUE;
122
0
}
123
124
static gboolean
125
fu_fit_firmware_verify_checksum(FuFitFirmware *self,
126
        FuFirmware *img,
127
        FuFirmware *img_hash,
128
        GChecksumType checksum_type,
129
        GBytes *blob,
130
        GError **error)
131
0
{
132
0
  gsize digest_len = g_checksum_type_get_length(checksum_type);
133
0
  g_autofree guint8 *buf = g_malloc0(digest_len);
134
0
  g_autoptr(GBytes) value = NULL;
135
0
  g_autoptr(GBytes) value_calc = NULL;
136
0
  g_autoptr(GChecksum) checksum = g_checksum_new(checksum_type);
137
138
  /* get value and verify */
139
0
  value = fu_fdt_image_get_attr(FU_FDT_IMAGE(img_hash), FU_FIT_FIRMWARE_ATTR_VALUE, error);
140
0
  if (value == NULL)
141
0
    return FALSE;
142
0
  if (g_bytes_get_size(value) != digest_len) {
143
0
    g_set_error(error,
144
0
          FWUPD_ERROR,
145
0
          FWUPD_ERROR_INVALID_DATA,
146
0
          "%s invalid hash value size, got 0x%x, expected 0x%x",
147
0
          fu_firmware_get_id(img),
148
0
          (guint)g_bytes_get_size(value),
149
0
          (guint)digest_len);
150
0
    return FALSE;
151
0
  }
152
0
  g_checksum_update(checksum,
153
0
        (const guchar *)g_bytes_get_data(value, NULL),
154
0
        g_bytes_get_size(value));
155
0
  g_checksum_get_digest(checksum, buf, &digest_len);
156
0
  value_calc = g_bytes_new(buf, digest_len);
157
0
  if (!fu_bytes_compare(value, value_calc, error))
158
0
    return FALSE;
159
160
  /* success */
161
0
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
162
0
  return TRUE;
163
0
}
164
165
static gboolean
166
fu_fit_firmware_verify_hash(FuFitFirmware *self,
167
          FuFirmware *img,
168
          FuFirmware *img_hash,
169
          GBytes *blob,
170
          GError **error)
171
0
{
172
0
  g_autofree gchar *algo = NULL;
173
174
  /* what is this */
175
0
  if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img_hash),
176
0
               FU_FIT_FIRMWARE_ATTR_ALGO,
177
0
               &algo,
178
0
               error)) {
179
0
    g_prefix_error(error, "cannot get algo for %s: ", fu_firmware_get_id(img));
180
0
    return FALSE;
181
0
  }
182
0
  if (g_strcmp0(algo, "crc32") == 0)
183
0
    return fu_fit_firmware_verify_crc32(self, img, img_hash, blob, error);
184
0
  if (g_strcmp0(algo, "md5") == 0) {
185
0
    return fu_fit_firmware_verify_checksum(self,
186
0
                   img,
187
0
                   img_hash,
188
0
                   G_CHECKSUM_MD5,
189
0
                   blob,
190
0
                   error);
191
0
  }
192
0
  if (g_strcmp0(algo, "sha1") == 0) {
193
0
    return fu_fit_firmware_verify_checksum(self,
194
0
                   img,
195
0
                   img_hash,
196
0
                   G_CHECKSUM_SHA1,
197
0
                   blob,
198
0
                   error);
199
0
  }
200
0
  if (g_strcmp0(algo, "sha256") == 0) {
201
0
    return fu_fit_firmware_verify_checksum(self,
202
0
                   img,
203
0
                   img_hash,
204
0
                   G_CHECKSUM_SHA256,
205
0
                   blob,
206
0
                   error);
207
0
  }
208
209
  /* ignore any hashes we do not support: success */
210
0
  return TRUE;
211
0
}
212
213
static gboolean
214
fu_fit_firmware_verify_image(FuFitFirmware *self,
215
           GInputStream *stream,
216
           FuFirmware *img,
217
           FuFirmwareParseFlags flags,
218
           GError **error)
219
73
{
220
73
  g_autoptr(GBytes) blob = NULL;
221
222
  /* sanity check */
223
73
  if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img), "type", NULL, error))
224
27
    return FALSE;
225
46
  if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img), "description", NULL, error))
226
5
    return FALSE;
227
228
  /* if has data */
229
41
  blob = fu_fdt_image_get_attr(FU_FDT_IMAGE(img), FU_FIT_FIRMWARE_ATTR_DATA, NULL);
230
41
  if (blob == NULL) {
231
2
    guint32 data_size = 0x0;
232
2
    guint32 data_offset = 0x0;
233
234
    /* extra data outside of FIT image */
235
2
    if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img),
236
2
                 FU_FIT_FIRMWARE_ATTR_DATA_OFFSET,
237
2
                 &data_offset,
238
2
                 error))
239
1
      return FALSE;
240
1
    if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img),
241
1
                 FU_FIT_FIRMWARE_ATTR_DATA_SIZE,
242
1
                 &data_size,
243
1
                 error))
244
1
      return FALSE;
245
0
    blob = fu_input_stream_read_bytes(stream, data_offset, data_size, NULL, error);
246
0
    if (blob == NULL)
247
0
      return FALSE;
248
0
  }
249
39
  fu_dump_bytes(G_LOG_DOMAIN, "data", blob);
250
251
  /* verify any hashes we recognize */
252
39
  if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) {
253
0
    g_autoptr(GPtrArray) img_hashes = fu_firmware_get_images(img);
254
0
    for (guint i = 0; i < img_hashes->len; i++) {
255
0
      FuFirmware *img_hash = g_ptr_array_index(img_hashes, i);
256
0
      if (fu_firmware_get_id(img_hash) == NULL) {
257
0
        g_set_error_literal(error,
258
0
                FWUPD_ERROR,
259
0
                FWUPD_ERROR_INVALID_DATA,
260
0
                "no ID for image hash");
261
0
        return FALSE;
262
0
      }
263
0
      if (g_str_has_prefix(fu_firmware_get_id(img_hash), "hash")) {
264
0
        if (!fu_fit_firmware_verify_hash(self, img, img_hash, blob, error))
265
0
          return FALSE;
266
0
      }
267
0
    }
268
0
  }
269
270
  /* success */
271
39
  return TRUE;
272
39
}
273
274
static gboolean
275
fu_fit_firmware_verify_configuration(FuFitFirmware *self,
276
             FuFirmware *img,
277
             FuFirmwareParseFlags flags,
278
             GError **error)
279
32
{
280
  /* sanity check */
281
32
  if (!fu_fdt_image_get_attr_strlist(FU_FDT_IMAGE(img),
282
32
             FU_FIT_FIRMWARE_ATTR_COMPATIBLE,
283
32
             NULL,
284
32
             error))
285
19
    return FALSE;
286
287
  /* success */
288
13
  return TRUE;
289
32
}
290
291
static gboolean
292
fu_fit_firmware_parse(FuFirmware *firmware,
293
          GInputStream *stream,
294
          FuFirmwareParseFlags flags,
295
          GError **error)
296
1.92k
{
297
1.92k
  FuFitFirmware *self = FU_FIT_FIRMWARE(firmware);
298
1.92k
  g_autoptr(FuFirmware) img_cfgs = NULL;
299
1.92k
  g_autoptr(FuFirmware) img_images = NULL;
300
1.92k
  g_autoptr(FuFirmware) img_root = NULL;
301
1.92k
  g_autoptr(GPtrArray) img_images_array = NULL;
302
1.92k
  g_autoptr(GPtrArray) img_cfgs_array = NULL;
303
304
  /* FuFdtFirmware->parse */
305
1.92k
  if (!FU_FIRMWARE_CLASS(fu_fit_firmware_parent_class)->parse(firmware, stream, flags, error))
306
837
    return FALSE;
307
308
  /* sanity check */
309
1.08k
  img_root = fu_firmware_get_image_by_id(firmware, NULL, error);
310
1.08k
  if (img_root == NULL)
311
131
    return FALSE;
312
958
  if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img_root),
313
958
               FU_FIT_FIRMWARE_ATTR_TIMESTAMP,
314
958
               NULL,
315
958
               error))
316
204
    return FALSE;
317
318
  /* check the checksums of each image */
319
754
  img_images = fu_firmware_get_image_by_id(img_root, FU_FIT_FIRMWARE_ID_IMAGES, error);
320
754
  if (img_images == NULL)
321
134
    return FALSE;
322
620
  img_images_array = fu_firmware_get_images(img_images);
323
659
  for (guint i = 0; i < img_images_array->len; i++) {
324
73
    FuFirmware *img = g_ptr_array_index(img_images_array, i);
325
73
    if (!fu_fit_firmware_verify_image(self, stream, img, flags, error))
326
34
      return FALSE;
327
73
  }
328
329
  /* check the setup of each configuration */
330
586
  img_cfgs = fu_firmware_get_image_by_id(img_root, FU_FIT_FIRMWARE_ID_CONFIGURATIONS, error);
331
586
  if (img_cfgs == NULL)
332
98
    return FALSE;
333
488
  img_cfgs_array = fu_firmware_get_images(img_cfgs);
334
501
  for (guint i = 0; i < img_cfgs_array->len; i++) {
335
32
    FuFirmware *img = g_ptr_array_index(img_cfgs_array, i);
336
32
    if (!fu_fit_firmware_verify_configuration(self, img, flags, error))
337
19
      return FALSE;
338
32
  }
339
340
  /* success */
341
469
  return TRUE;
342
488
}
343
344
static void
345
fu_fit_firmware_init(FuFitFirmware *self)
346
1.99k
{
347
1.99k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 1024);
348
1.99k
}
349
350
static void
351
fu_fit_firmware_class_init(FuFitFirmwareClass *klass)
352
1
{
353
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
354
1
  firmware_class->parse = fu_fit_firmware_parse;
355
1
}
356
357
/**
358
 * fu_fit_firmware_new:
359
 *
360
 * Creates a new #FuFirmware of sub type FIT
361
 *
362
 * Since: 1.8.2
363
 **/
364
FuFirmware *
365
fu_fit_firmware_new(void)
366
0
{
367
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_FIT_FIRMWARE, NULL));
368
0
}