Coverage Report

Created: 2026-10-28 07:47

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
62.9k
G_DEFINE_TYPE(FuEfiVss2VariableStore, fu_efi_vss2_variable_store, FU_TYPE_FIRMWARE)
32
62.9k
33
62.9k
static gboolean
34
62.9k
fu_efi_vss2_variable_store_validate(FuFirmware *firmware,
35
62.9k
            FuInputStream *stream,
36
62.9k
            gsize offset,
37
62.9k
            GError **error)
38
62.9k
{
39
62.9k
  return fu_struct_efi_vss2_variable_store_header_validate_stream(stream, offset, error);
40
62.9k
}
41
42
static gboolean
43
fu_efi_vss2_variable_store_parse(FuFirmware *firmware,
44
         FuInputStream *stream,
45
         FuFirmwareParseFlags flags,
46
         GError **error)
47
2.44k
{
48
2.44k
  gsize offset = 0x0;
49
2.44k
  g_autoptr(FuStructEfiVss2VariableStoreHeader) st = NULL;
50
51
2.44k
  st = fu_struct_efi_vss2_variable_store_header_parse_stream(stream, offset, error);
52
2.44k
  if (st == NULL)
53
0
    return FALSE;
54
55
  /* sanity check */
56
2.44k
  if (fu_struct_efi_vss2_variable_store_header_get_size(st) >
57
2.44k
      fu_firmware_get_size_max(firmware)) {
58
63
    g_set_error(error,
59
63
          FWUPD_ERROR,
60
63
          FWUPD_ERROR_INTERNAL,
61
63
          "VSS store larger than max size: 0x%x > 0x%x",
62
63
          (guint)fu_struct_efi_vss2_variable_store_header_get_size(st),
63
63
          (guint)fu_firmware_get_size_max(firmware));
64
63
    return FALSE;
65
63
  }
66
67
  /* parse each attr */
68
2.38k
  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
14.8k
  while (offset < fu_struct_efi_vss2_variable_store_header_get_size(st)) {
74
14.1k
    g_autoptr(FuFirmware) img = fu_efi_vss_auth_variable_new();
75
76
14.1k
    if (!fu_firmware_parse_stream(img, stream, offset, flags, error)) {
77
1.38k
      g_prefix_error(error, "offset @0x%x: ", (guint)offset);
78
1.38k
      return FALSE;
79
1.38k
    }
80
12.7k
    if (fu_firmware_has_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE))
81
251
      break;
82
12.5k
    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
12.5k
    if (fu_efi_vss_auth_variable_get_state(FU_EFI_VSS_AUTH_VARIABLE(img)) ==
90
12.5k
        FU_EFI_VARIABLE_STATE_VARIABLE_ADDED) {
91
11.4k
      fu_firmware_set_offset(img, offset);
92
11.4k
      if (!fu_firmware_add_image(firmware, img, error))
93
74
        return FALSE;
94
11.4k
    }
95
12.4k
    if (!fu_size_checked_inc(&offset, fu_firmware_get_size(img), error))
96
0
      return FALSE;
97
12.4k
    offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4);
98
12.4k
  }
99
100
  /* success */
101
926
  fu_firmware_set_size(firmware, fu_struct_efi_vss2_variable_store_header_get_size(st));
102
926
  return TRUE;
103
2.38k
}
104
105
static GByteArray *
106
fu_efi_vss2_variable_store_write(FuFirmware *firmware, GError **error)
107
568
{
108
568
  g_autoptr(FuStructEfiVss2VariableStoreHeader) st =
109
568
      fu_struct_efi_vss2_variable_store_header_new();
110
568
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
111
112
  /* sanity check */
113
568
  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.49k
  for (guint i = 0; i < imgs->len; i++) {
123
1.94k
    FuFirmware *img = g_ptr_array_index(imgs, i);
124
1.94k
    g_autoptr(GByteArray) buf_tmp = NULL;
125
126
1.94k
    buf_tmp = fu_firmware_write_array(img, error);
127
1.94k
    if (buf_tmp == NULL)
128
0
      return NULL;
129
1.94k
    fu_byte_array_append_array(st->buf, buf_tmp);
130
1.94k
    fu_byte_array_align_up(st->buf, FU_FIRMWARE_ALIGNMENT_4, 0xFF);
131
1.94k
  }
132
133
  /* sanity check */
134
558
  if (st->buf->len > fu_firmware_get_size(firmware)) {
135
90
    g_set_error(error,
136
90
          FWUPD_ERROR,
137
90
          FWUPD_ERROR_INTERNAL,
138
90
          "VSS2 store is too small, needed 0x%x but defined as 0x%x",
139
90
          st->buf->len,
140
90
          (guint)(fu_firmware_get_size(firmware)));
141
90
    return NULL;
142
90
  }
143
144
  /* fix up header and attrs */
145
468
  fu_byte_array_set_size(st->buf, fu_firmware_get_size(firmware), 0xFF);
146
468
  fu_struct_efi_vss2_variable_store_header_set_size(st, fu_firmware_get_size(firmware));
147
148
  /* success */
149
468
  return g_steal_pointer(&st->buf);
150
558
}
151
152
static void
153
fu_efi_vss2_variable_store_init(FuEfiVss2VariableStore *self)
154
62.9k
{
155
62.9k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID);
156
62.9k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
157
62.9k
}
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
}