Coverage Report

Created: 2026-04-12 07:04

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-string.h"
17
18
/**
19
 * FuEfiVss2VariableStore:
20
 *
21
 * A NVRAM variable store.
22
 *
23
 * See also: [class@FuFirmware]
24
 */
25
26
struct _FuEfiVss2VariableStore {
27
  FuFirmware parent_instance;
28
};
29
30
144k
G_DEFINE_TYPE(FuEfiVss2VariableStore, fu_efi_vss2_variable_store, FU_TYPE_FIRMWARE)
31
144k
32
144k
static gboolean
33
144k
fu_efi_vss2_variable_store_validate(FuFirmware *firmware,
34
144k
            GInputStream *stream,
35
144k
            gsize offset,
36
144k
            GError **error)
37
144k
{
38
109k
  return fu_struct_efi_vss2_variable_store_header_validate_stream(stream, offset, error);
39
109k
}
40
41
static gboolean
42
fu_efi_vss2_variable_store_parse(FuFirmware *firmware,
43
         GInputStream *stream,
44
         FuFirmwareParseFlags flags,
45
         GError **error)
46
2.67k
{
47
2.67k
  gsize offset = 0x0;
48
2.67k
  g_autoptr(FuStructEfiVss2VariableStoreHeader) st = NULL;
49
50
2.67k
  st = fu_struct_efi_vss2_variable_store_header_parse_stream(stream, offset, error);
51
2.67k
  if (st == NULL)
52
0
    return FALSE;
53
54
  /* sanity check */
55
2.67k
  if (fu_struct_efi_vss2_variable_store_header_get_size(st) >
56
2.67k
      fu_firmware_get_size_max(firmware)) {
57
72
    g_set_error(error,
58
72
          FWUPD_ERROR,
59
72
          FWUPD_ERROR_INTERNAL,
60
72
          "VSS store larger than max size: 0x%x > 0x%x",
61
72
          (guint)fu_struct_efi_vss2_variable_store_header_get_size(st),
62
72
          (guint)fu_firmware_get_size_max(firmware));
63
72
    return FALSE;
64
72
  }
65
66
  /* parse each attr */
67
2.60k
  if (!fu_size_checked_inc(&offset, st->buf->len, error)) {
68
0
    g_prefix_error_literal(error, "VSS2 header offset overflow: ");
69
0
    return FALSE;
70
0
  }
71
72
15.9k
  while (offset < fu_struct_efi_vss2_variable_store_header_get_size(st)) {
73
15.2k
    g_autoptr(FuFirmware) img = fu_efi_vss_auth_variable_new();
74
75
15.2k
    if (!fu_firmware_parse_stream(img, stream, offset, flags, error)) {
76
1.57k
      g_prefix_error(error, "offset @0x%x: ", (guint)offset);
77
1.57k
      return FALSE;
78
1.57k
    }
79
13.7k
    if (fu_firmware_has_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE))
80
292
      break;
81
13.4k
    if (fu_firmware_get_size(img) == 0) {
82
0
      g_set_error_literal(error,
83
0
              FWUPD_ERROR,
84
0
              FWUPD_ERROR_INTERNAL,
85
0
              "VSS2 store entry has zero size");
86
0
      return FALSE;
87
0
    }
88
13.4k
    if (fu_efi_vss_auth_variable_get_state(FU_EFI_VSS_AUTH_VARIABLE(img)) ==
89
13.4k
        FU_EFI_VARIABLE_STATE_VARIABLE_ADDED) {
90
13.1k
      fu_firmware_set_offset(img, offset);
91
13.1k
      if (!fu_firmware_add_image(firmware, img, error))
92
46
        return FALSE;
93
13.1k
    }
94
13.3k
    if (!fu_size_checked_inc(&offset, fu_firmware_get_size(img), error))
95
0
      return FALSE;
96
13.3k
    offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4);
97
13.3k
  }
98
99
  /* success */
100
986
  fu_firmware_set_size(firmware, fu_struct_efi_vss2_variable_store_header_get_size(st));
101
986
  return TRUE;
102
2.60k
}
103
104
static GByteArray *
105
fu_efi_vss2_variable_store_write(FuFirmware *firmware, GError **error)
106
625
{
107
625
  g_autoptr(FuStructEfiVss2VariableStoreHeader) st =
108
625
      fu_struct_efi_vss2_variable_store_header_new();
109
625
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
110
111
  /* sanity check */
112
625
  if (fu_firmware_get_size(firmware) < st->buf->len) {
113
12
    g_set_error_literal(error,
114
12
            FWUPD_ERROR,
115
12
            FWUPD_ERROR_INTERNAL,
116
12
            "VSS2 variable store has zero size");
117
12
    return NULL;
118
12
  }
119
120
  /* each attr */
121
2.53k
  for (guint i = 0; i < imgs->len; i++) {
122
1.92k
    FuFirmware *img = g_ptr_array_index(imgs, i);
123
1.92k
    g_autoptr(GBytes) fw = NULL;
124
125
1.92k
    fw = fu_firmware_write(img, error);
126
1.92k
    if (fw == NULL)
127
0
      return NULL;
128
1.92k
    fu_byte_array_append_bytes(st->buf, fw);
129
1.92k
    fu_byte_array_align_up(st->buf, FU_FIRMWARE_ALIGNMENT_4, 0xFF);
130
1.92k
  }
131
132
  /* sanity check */
133
613
  if (st->buf->len > fu_firmware_get_size(firmware)) {
134
113
    g_set_error(error,
135
113
          FWUPD_ERROR,
136
113
          FWUPD_ERROR_INTERNAL,
137
113
          "VSS2 store is too small, needed 0x%x but defined as 0x%x",
138
113
          st->buf->len,
139
113
          (guint)(fu_firmware_get_size(firmware)));
140
113
    return NULL;
141
113
  }
142
143
  /* fix up header and attrs */
144
500
  fu_byte_array_set_size(st->buf, fu_firmware_get_size(firmware), 0xFF);
145
500
  fu_struct_efi_vss2_variable_store_header_set_size(st, fu_firmware_get_size(firmware));
146
147
  /* success */
148
500
  return g_steal_pointer(&st->buf);
149
613
}
150
151
static void
152
fu_efi_vss2_variable_store_init(FuEfiVss2VariableStore *self)
153
109k
{
154
109k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_VSS_AUTH_VARIABLE);
155
109k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID);
156
109k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
157
109k
#ifdef HAVE_FUZZER
158
109k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 10);
159
109k
  fu_firmware_set_size_max(FU_FIRMWARE(self), 4 * FU_KB);
160
#else
161
  fu_firmware_set_images_max(FU_FIRMWARE(self), 10000);
162
  fu_firmware_set_size_max(FU_FIRMWARE(self), 16 * FU_MB);
163
#endif
164
109k
}
165
166
static void
167
fu_efi_vss2_variable_store_class_init(FuEfiVss2VariableStoreClass *klass)
168
2
{
169
2
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
170
2
  firmware_class->validate = fu_efi_vss2_variable_store_validate;
171
2
  firmware_class->parse = fu_efi_vss2_variable_store_parse;
172
2
  firmware_class->write = fu_efi_vss2_variable_store_write;
173
2
}
174
175
/**
176
 * fu_efi_vss2_variable_store_new:
177
 *
178
 * Creates an empty VSS variable store.
179
 *
180
 * Returns: a #FuFirmware
181
 *
182
 * Since: 2.0.17
183
 **/
184
FuFirmware *
185
fu_efi_vss2_variable_store_new(void)
186
0
{
187
0
  return g_object_new(FU_TYPE_EFI_VSS2_VARIABLE_STORE, NULL);
188
0
}