Coverage Report

Created: 2026-08-15 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-efi-vss2-variable-store.c
Line
Count
Source
1
/*
2
 * Copyright 2025 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#define G_LOG_DOMAIN "FuEfiVss2VariableStore"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-common.h"
13
#include "fu-efi-struct.h"
14
#include "fu-efi-vss-auth-variable.h"
15
#include "fu-efi-vss2-variable-store.h"
16
#include "fu-input-stream.h"
17
#include "fu-string.h"
18
19
/**
20
 * FuEfiVss2VariableStore:
21
 *
22
 * A NVRAM variable store.
23
 *
24
 * See also: [class@FuFirmware]
25
 */
26
27
struct _FuEfiVss2VariableStore {
28
  FuFirmware parent_instance;
29
};
30
31
115k
G_DEFINE_TYPE(FuEfiVss2VariableStore, fu_efi_vss2_variable_store, FU_TYPE_FIRMWARE)
32
115k
33
115k
static gboolean
34
115k
fu_efi_vss2_variable_store_validate(FuFirmware *firmware,
35
115k
            FuInputStream *stream,
36
115k
            gsize offset,
37
115k
            GError **error)
38
115k
{
39
115k
  return fu_struct_efi_vss2_variable_store_header_validate_stream(stream, offset, error);
40
115k
}
41
42
static gboolean
43
fu_efi_vss2_variable_store_parse(FuFirmware *firmware,
44
         FuInputStream *stream,
45
         FuFirmwareParseFlags flags,
46
         GError **error)
47
2.84k
{
48
2.84k
  gsize offset = 0x0;
49
2.84k
  g_autoptr(FuStructEfiVss2VariableStoreHeader) st = NULL;
50
51
2.84k
  st = fu_struct_efi_vss2_variable_store_header_parse_stream(stream, offset, error);
52
2.84k
  if (st == NULL)
53
0
    return FALSE;
54
55
  /* sanity check */
56
2.84k
  if (fu_struct_efi_vss2_variable_store_header_get_size(st) >
57
2.84k
      fu_firmware_get_size_max(firmware)) {
58
80
    g_set_error(error,
59
80
          FWUPD_ERROR,
60
80
          FWUPD_ERROR_INTERNAL,
61
80
          "VSS store larger than max size: 0x%x > 0x%x",
62
80
          (guint)fu_struct_efi_vss2_variable_store_header_get_size(st),
63
80
          (guint)fu_firmware_get_size_max(firmware));
64
80
    return FALSE;
65
80
  }
66
67
  /* parse each attr */
68
2.76k
  if (!fu_size_checked_inc(&offset, st->buf->len, error)) {
69
0
    g_prefix_error_literal(error, "VSS2 header offset overflow: ");
70
0
    return FALSE;
71
0
  }
72
73
18.8k
  while (offset < fu_struct_efi_vss2_variable_store_header_get_size(st)) {
74
18.1k
    g_autoptr(FuFirmware) img = fu_efi_vss_auth_variable_new();
75
76
18.1k
    if (!fu_firmware_parse_stream(img, stream, offset, flags, error)) {
77
1.70k
      g_prefix_error(error, "offset @0x%x: ", (guint)offset);
78
1.70k
      return FALSE;
79
1.70k
    }
80
16.4k
    if (fu_firmware_has_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE))
81
236
      break;
82
16.1k
    if (fu_firmware_get_size(img) == 0) {
83
0
      g_set_error_literal(error,
84
0
              FWUPD_ERROR,
85
0
              FWUPD_ERROR_INTERNAL,
86
0
              "VSS2 store entry has zero size");
87
0
      return FALSE;
88
0
    }
89
16.1k
    if (fu_efi_vss_auth_variable_get_state(FU_EFI_VSS_AUTH_VARIABLE(img)) ==
90
16.1k
        FU_EFI_VARIABLE_STATE_VARIABLE_ADDED) {
91
15.5k
      fu_firmware_set_offset(img, offset);
92
15.5k
      if (!fu_firmware_add_image(firmware, img, error))
93
105
        return FALSE;
94
15.5k
    }
95
16.0k
    if (!fu_size_checked_inc(&offset, fu_firmware_get_size(img), error))
96
0
      return FALSE;
97
16.0k
    offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4);
98
16.0k
  }
99
100
  /* success */
101
954
  fu_firmware_set_size(firmware, fu_struct_efi_vss2_variable_store_header_get_size(st));
102
954
  return TRUE;
103
2.76k
}
104
105
static GByteArray *
106
fu_efi_vss2_variable_store_write(FuFirmware *firmware, GError **error)
107
552
{
108
552
  g_autoptr(FuStructEfiVss2VariableStoreHeader) st =
109
552
      fu_struct_efi_vss2_variable_store_header_new();
110
552
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
111
112
  /* sanity check */
113
552
  if (fu_firmware_get_size(firmware) < st->buf->len) {
114
10
    g_set_error_literal(error,
115
10
            FWUPD_ERROR,
116
10
            FWUPD_ERROR_INTERNAL,
117
10
            "VSS2 variable store has zero size");
118
10
    return NULL;
119
10
  }
120
121
  /* each attr */
122
2.55k
  for (guint i = 0; i < imgs->len; i++) {
123
2.01k
    FuFirmware *img = g_ptr_array_index(imgs, i);
124
2.01k
    g_autoptr(GBytes) fw = NULL;
125
126
2.01k
    fw = fu_firmware_write(img, error);
127
2.01k
    if (fw == NULL)
128
0
      return NULL;
129
2.01k
    fu_byte_array_append_bytes(st->buf, fw);
130
2.01k
    fu_byte_array_align_up(st->buf, FU_FIRMWARE_ALIGNMENT_4, 0xFF);
131
2.01k
  }
132
133
  /* sanity check */
134
542
  if (st->buf->len > fu_firmware_get_size(firmware)) {
135
110
    g_set_error(error,
136
110
          FWUPD_ERROR,
137
110
          FWUPD_ERROR_INTERNAL,
138
110
          "VSS2 store is too small, needed 0x%x but defined as 0x%x",
139
110
          st->buf->len,
140
110
          (guint)(fu_firmware_get_size(firmware)));
141
110
    return NULL;
142
110
  }
143
144
  /* fix up header and attrs */
145
432
  fu_byte_array_set_size(st->buf, fu_firmware_get_size(firmware), 0xFF);
146
432
  fu_struct_efi_vss2_variable_store_header_set_size(st, fu_firmware_get_size(firmware));
147
148
  /* success */
149
432
  return g_steal_pointer(&st->buf);
150
542
}
151
152
static void
153
fu_efi_vss2_variable_store_init(FuEfiVss2VariableStore *self)
154
115k
{
155
115k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID);
156
115k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
157
115k
}
158
159
static void
160
fu_efi_vss2_variable_store_class_init(FuEfiVss2VariableStoreClass *klass)
161
3
{
162
3
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
163
3
  firmware_class->validate = fu_efi_vss2_variable_store_validate;
164
3
  firmware_class->parse = fu_efi_vss2_variable_store_parse;
165
3
  firmware_class->write = fu_efi_vss2_variable_store_write;
166
3
  fu_firmware_add_image_gtype(firmware_class, FU_TYPE_EFI_VSS_AUTH_VARIABLE);
167
3
#ifdef HAVE_FUZZER
168
3
  fu_firmware_set_size_max(firmware_class, 4 * FU_KB);
169
3
  fu_firmware_set_images_max(firmware_class, 10);
170
#else
171
  fu_firmware_set_size_max(firmware_class, 16 * FU_MB);
172
  fu_firmware_set_images_max(firmware_class, 10000);
173
#endif
174
3
}
175
176
/**
177
 * fu_efi_vss2_variable_store_new:
178
 *
179
 * Creates an empty VSS variable store.
180
 *
181
 * Returns: a #FuFirmware
182
 *
183
 * Since: 2.0.17
184
 **/
185
FuFirmware *
186
fu_efi_vss2_variable_store_new(void)
187
0
{
188
0
  return g_object_new(FU_TYPE_EFI_VSS2_VARIABLE_STORE, NULL);
189
0
}