Coverage Report

Created: 2025-08-03 06:57

/src/fwupd/libfwupdplugin/fu-efi-lz77-decompressor.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2024 Richard Hughes <richard@hughsie.com>
3
 * Copyright 2018 LongSoft
4
 * Copyright 2008 Apple Inc
5
 * Copyright 2006 Intel Corporation
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause or LGPL-2.1-or-later
8
 */
9
10
0
#define G_LOG_DOMAIN "FuFirmware"
11
12
#include "config.h"
13
14
#include "fu-byte-array.h"
15
#include "fu-efi-lz77-decompressor.h"
16
#include "fu-input-stream.h"
17
18
struct _FuEfiLz77Decompressor {
19
  FuFirmware parent_instance;
20
};
21
22
/**
23
 * FuEfiLz77Decompressor:
24
 *
25
 * Funky LZ77 decompressor as specified by EFI. The compression design [and code] was designed for
26
 * a different era, and much better compression can be achieved using LZMA or zlib.
27
 *
28
 * My advice would be to not use this compression method in new designs.
29
 *
30
 * See also: [class@FuFirmware]
31
 */
32
33
G_DEFINE_TYPE(FuEfiLz77Decompressor, fu_efi_lz77_decompressor, FU_TYPE_FIRMWARE)
34
35
571M
#define BITBUFSIZ 32
36
565M
#define MAXMATCH  256
37
573M
#define THRESHOLD 3
38
4.47M
#define CODE_BIT  16
39
40
/* c: char&len set; p: position set; t: extra set */
41
565M
#define NC  (0xff + MAXMATCH + 2 - THRESHOLD)
42
193k
#define CBIT  9
43
11.7M
#define MAXPBIT 5
44
138k
#define TBIT  5
45
11.7M
#define MAXNP ((1U << MAXPBIT) - 1)
46
4.47M
#define NT  (CODE_BIT + 3)
47
#if NT > MAXNP
48
#define NPT NT
49
#else
50
2.60M
#define NPT MAXNP
51
#endif
52
53
typedef struct {
54
  GInputStream *stream; /* no-ref */
55
  GByteArray *dst;      /* no-ref */
56
57
  guint16 bit_count;
58
  guint32 bit_buf;
59
  guint32 sub_bit_buf;
60
  guint16 block_size;
61
62
  guint16 left[2 * NC - 1];
63
  guint16 right[2 * NC - 1];
64
  guint8 c_len[NC];
65
  guint8 pt_len[NPT];
66
  guint16 c_table[4096];
67
  guint16 pt_table[256];
68
69
  guint8 p_bit; /* 'position set code length array size' in block header */
70
} FuEfiLz77DecompressHelper;
71
72
static void
73
fu_efi_lz77_decompressor_memset16(guint16 *buf, gsize length, guint16 value)
74
222k
{
75
222k
  g_return_if_fail(length % 2 == 0);
76
222k
  length /= sizeof(guint16);
77
250M
  for (gsize i = 0; i < length; i++)
78
250M
    buf[i] = value;
79
222k
}
80
81
static gboolean
82
fu_efi_lz77_decompressor_read_source_bits(FuEfiLz77DecompressHelper *helper,
83
            guint16 number_of_bits,
84
            GError **error)
85
538M
{
86
  /* left shift number_of_bits of bits in advance */
87
538M
  helper->bit_buf = (guint32)(((guint64)helper->bit_buf) << number_of_bits);
88
89
  /* copy data needed in bytes into sub_bit_buf */
90
543M
  while (number_of_bits > helper->bit_count) {
91
5.00M
    gssize rc;
92
5.00M
    guint8 sub_bit_buf = 0;
93
94
5.00M
    number_of_bits = (guint16)(number_of_bits - helper->bit_count);
95
5.00M
    helper->bit_buf |= (guint32)(((guint64)helper->sub_bit_buf) << number_of_bits);
96
97
    /* get 1 byte into sub_bit_buf */
98
5.00M
    rc = g_input_stream_read(helper->stream,
99
5.00M
           &sub_bit_buf,
100
5.00M
           sizeof(sub_bit_buf),
101
5.00M
           NULL,
102
5.00M
           error);
103
5.00M
    if (rc < 0)
104
0
      return FALSE;
105
5.00M
    if (rc == 0) {
106
      /* no more bits from the source, just pad zero bit */
107
3.47M
      helper->sub_bit_buf = 0;
108
3.47M
    } else {
109
1.52M
      helper->sub_bit_buf = sub_bit_buf;
110
1.52M
    }
111
5.00M
    helper->bit_count = 8;
112
5.00M
  }
113
114
  /* calculate additional bit count read to update bit_count */
115
538M
  helper->bit_count = (guint16)(helper->bit_count - number_of_bits);
116
117
  /* copy number_of_bits of bits from sub_bit_buf into bit_buf */
118
538M
  helper->bit_buf |= helper->sub_bit_buf >> helper->bit_count;
119
538M
  return TRUE;
120
538M
}
121
122
static gboolean
123
fu_efi_lz77_decompressor_get_bits(FuEfiLz77DecompressHelper *helper,
124
          guint16 number_of_bits,
125
          guint16 *value,
126
          GError **error)
127
2.58M
{
128
  /* pop number_of_bits of bits from left */
129
2.58M
  *value = (guint16)(helper->bit_buf >> (BITBUFSIZ - number_of_bits));
130
131
  /* fill up bit_buf from source */
132
2.58M
  return fu_efi_lz77_decompressor_read_source_bits(helper, number_of_bits, error);
133
2.58M
}
134
135
/* creates huffman code mapping table for extra set, char&len set and position set according to
136
 * code length array */
137
static gboolean
138
fu_efi_lz77_decompressor_make_huffman_table(FuEfiLz77DecompressHelper *helper,
139
              guint16 number_of_symbols,
140
              guint8 *code_length_array,
141
              guint16 mapping_table_bits,
142
              guint16 *table,
143
              GError **error)
144
192k
{
145
192k
  guint16 count[17] = {0};
146
192k
  guint16 weight[17] = {0};
147
192k
  guint16 start[18] = {0};
148
192k
  guint16 *pointer;
149
192k
  guint16 index;
150
192k
  guint16 c_char;
151
192k
  guint16 ju_bits;
152
192k
  guint16 avail_symbols;
153
192k
  guint16 mask;
154
192k
  guint16 max_table_length;
155
156
  /* the maximum mapping table width supported by this internal working function is 16 */
157
192k
  if (mapping_table_bits >= (sizeof(count) / sizeof(guint16))) {
158
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bad table");
159
0
    return FALSE;
160
0
  }
161
162
47.5M
  for (index = 0; index < number_of_symbols; index++) {
163
47.3M
    if (code_length_array[index] > 16) {
164
165
      g_set_error_literal(error,
165
165
              FWUPD_ERROR,
166
165
              FWUPD_ERROR_INVALID_DATA,
167
165
              "bad table");
168
165
      return FALSE;
169
165
    }
170
47.3M
    count[code_length_array[index]]++;
171
47.3M
  }
172
173
3.27M
  for (index = 1; index <= 16; index++) {
174
3.08M
    guint16 WordOfStart = start[index];
175
3.08M
    guint16 WordOfCount = count[index];
176
3.08M
    start[index + 1] = (guint16)(WordOfStart + (WordOfCount << (16 - index)));
177
3.08M
  }
178
179
192k
  if (start[17] != 0) {
180
    /*(1U << 16)*/
181
1.50k
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bad table");
182
1.50k
    return FALSE;
183
1.50k
  }
184
185
191k
  ju_bits = (guint16)(16 - mapping_table_bits);
186
2.07M
  for (index = 1; index <= mapping_table_bits; index++) {
187
1.87M
    start[index] >>= ju_bits;
188
1.87M
    weight[index] = (guint16)(1U << (mapping_table_bits - index));
189
1.87M
  }
190
191
1.37M
  while (index <= 16) {
192
1.17M
    weight[index] = (guint16)(1U << (16 - index));
193
1.17M
    index++;
194
1.17M
  }
195
196
191k
  index = (guint16)(start[mapping_table_bits + 1] >> ju_bits);
197
191k
  if (index != 0) {
198
425
    guint16 index3 = (guint16)(1U << mapping_table_bits);
199
425
    if (index < index3) {
200
425
      fu_efi_lz77_decompressor_memset16(table + index,
201
425
                (index3 - index) * sizeof(*table),
202
425
                0);
203
425
    }
204
425
  }
205
206
191k
  avail_symbols = number_of_symbols;
207
191k
  mask = (guint16)(1U << (15 - mapping_table_bits));
208
191k
  max_table_length = (guint16)(1U << mapping_table_bits);
209
210
47.3M
  for (c_char = 0; c_char < number_of_symbols; c_char++) {
211
47.1M
    guint16 len = code_length_array[c_char];
212
47.1M
    guint16 next_code;
213
214
47.1M
    if (len == 0 || len >= 17)
215
46.9M
      continue;
216
217
207k
    next_code = (guint16)(start[len] + weight[len]);
218
207k
    if (len <= mapping_table_bits) {
219
9.21M
      for (index = start[len]; index < next_code; index++) {
220
9.01M
        if (index >= max_table_length) {
221
106
          g_set_error_literal(error,
222
106
                  FWUPD_ERROR,
223
106
                  FWUPD_ERROR_INVALID_DATA,
224
106
                  "bad table");
225
106
          return FALSE;
226
106
        }
227
9.01M
        table[index] = c_char;
228
9.01M
      }
229
230
198k
    } else {
231
9.81k
      guint16 index3 = start[len];
232
9.81k
      pointer = &table[index3 >> ju_bits];
233
9.81k
      index = (guint16)(len - mapping_table_bits);
234
235
43.1k
      while (index != 0) {
236
33.3k
        if (*pointer == 0 && avail_symbols < (2 * NC - 1)) {
237
8.42k
          helper->right[avail_symbols] = helper->left[avail_symbols] =
238
8.42k
              0;
239
8.42k
          *pointer = avail_symbols++;
240
8.42k
        }
241
33.3k
        if (*pointer < (2 * NC - 1)) {
242
33.3k
          if ((index3 & mask) != 0)
243
16.8k
            pointer = &helper->right[*pointer];
244
16.4k
          else
245
16.4k
            pointer = &helper->left[*pointer];
246
33.3k
        }
247
33.3k
        index3 <<= 1;
248
33.3k
        index--;
249
33.3k
      }
250
9.81k
      *pointer = c_char;
251
9.81k
    }
252
207k
    start[len] = next_code;
253
207k
  }
254
  /* success */
255
191k
  return TRUE;
256
191k
}
257
258
/* get a position value according to Position Huffman table */
259
static gboolean
260
fu_efi_lz77_decompressor_decode_p(FuEfiLz77DecompressHelper *helper, guint32 *value, GError **error)
261
8.22M
{
262
8.22M
  guint16 val;
263
264
8.22M
  val = helper->pt_table[helper->bit_buf >> (BITBUFSIZ - 8)];
265
8.22M
  if (val >= MAXNP) {
266
811k
    guint32 mask = 1U << (BITBUFSIZ - 1 - 8);
267
817k
    do {
268
817k
      if ((helper->bit_buf & mask) != 0) {
269
422k
        val = helper->right[val];
270
422k
      } else {
271
394k
        val = helper->left[val];
272
394k
      }
273
817k
      mask >>= 1;
274
817k
    } while (val >= MAXNP);
275
811k
  }
276
277
  /* advance what we have read */
278
8.22M
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, helper->pt_len[val], error))
279
0
    return FALSE;
280
281
8.22M
  if (val > 1) {
282
1.75M
    guint16 char_c = 0;
283
1.75M
    if (!fu_efi_lz77_decompressor_get_bits(helper, (guint16)(val - 1), &char_c, error))
284
0
      return FALSE;
285
1.75M
    *value = (guint32)((1U << (val - 1)) + char_c);
286
1.75M
    return TRUE;
287
1.75M
  }
288
6.46M
  *value = val;
289
6.46M
  return TRUE;
290
8.22M
}
291
292
/* read in the extra set or position set length array, then generate the code mapping for them */
293
static gboolean
294
fu_efi_lz77_decompressor_read_pt_len(FuEfiLz77DecompressHelper *helper,
295
             guint16 number_of_symbols,
296
             guint16 number_of_bits,
297
             guint16 special_symbol,
298
             GError **error)
299
276k
{
300
276k
  guint16 number = 0;
301
276k
  guint16 index = 0;
302
303
  /* read Extra Set Code Length Array size */
304
276k
  if (!fu_efi_lz77_decompressor_get_bits(helper, number_of_bits, &number, error))
305
0
    return FALSE;
306
307
  /* fail if number or number_of_symbols is greater than size of pt_len */
308
276k
  if ((number > sizeof(helper->pt_len)) || (number_of_symbols > sizeof(helper->pt_len))) {
309
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bad table");
310
0
    return FALSE;
311
0
  }
312
276k
  if (number == 0) {
313
    /* this represents only Huffman code used */
314
171k
    guint16 char_c = 0;
315
171k
    if (!fu_efi_lz77_decompressor_get_bits(helper, number_of_bits, &char_c, error))
316
0
      return FALSE;
317
171k
    fu_efi_lz77_decompressor_memset16(&helper->pt_table[0],
318
171k
              sizeof(helper->pt_table),
319
171k
              (guint16)char_c);
320
171k
    memset(helper->pt_len, 0, number_of_symbols);
321
171k
    return TRUE;
322
171k
  }
323
324
375k
  while (index < number && index < NPT) {
325
270k
    guint16 char_c = helper->bit_buf >> (BITBUFSIZ - 3);
326
327
    /* if a code length is less than 7, then it is encoded as a 3-bit value.
328
     * Or it is encoded as a series of "1"s followed by a terminating "0".
329
     * The number of "1"s = Code length - 4 */
330
270k
    if (char_c == 7) {
331
4.36k
      guint32 mask = 1U << (BITBUFSIZ - 1 - 3);
332
22.3k
      while (mask & helper->bit_buf) {
333
17.9k
        mask >>= 1;
334
17.9k
        char_c += 1;
335
17.9k
      }
336
4.36k
    }
337
338
270k
    if (!fu_efi_lz77_decompressor_read_source_bits(
339
270k
      helper,
340
270k
      (guint16)((char_c < 7) ? 3 : char_c - 3),
341
270k
      error))
342
0
      return FALSE;
343
344
270k
    helper->pt_len[index++] = (guint8)char_c;
345
346
    /* for code&len set, after the third length of the code length concatenation,
347
     * a 2-bit value is used to indicated the number of consecutive zero lengths after
348
     * the third length */
349
270k
    if (index == special_symbol) {
350
5.10k
      if (!fu_efi_lz77_decompressor_get_bits(helper, 2, &char_c, error))
351
0
        return FALSE;
352
6.89k
      while ((gint16)(--char_c) >= 0 && index < NPT) {
353
1.79k
        helper->pt_len[index++] = 0;
354
1.79k
      }
355
5.10k
    }
356
270k
  }
357
2.43M
  while (index < number_of_symbols && index < NPT)
358
2.32M
    helper->pt_len[index++] = 0;
359
105k
  return fu_efi_lz77_decompressor_make_huffman_table(helper,
360
105k
                 number_of_symbols,
361
105k
                 helper->pt_len,
362
105k
                 8,
363
105k
                 helper->pt_table,
364
105k
                 error);
365
105k
}
366
367
/* read in and decode the Char&len Set Code Length Array, then generate the Huffman Code mapping
368
 * table for the char&len set */
369
static gboolean
370
fu_efi_lz77_decompressor_read_c_len(FuEfiLz77DecompressHelper *helper, GError **error)
371
138k
{
372
138k
  guint16 number = 0;
373
138k
  guint16 index = 0;
374
375
138k
  if (!fu_efi_lz77_decompressor_get_bits(helper, CBIT, &number, error))
376
0
    return FALSE;
377
138k
  if (number == 0) {
378
    /* this represents only Huffman code used */
379
50.3k
    guint16 char_c = 0;
380
50.3k
    if (!fu_efi_lz77_decompressor_get_bits(helper, CBIT, &char_c, error))
381
0
      return FALSE;
382
50.3k
    memset(helper->c_len, 0, sizeof(helper->c_len));
383
50.3k
    fu_efi_lz77_decompressor_memset16(&helper->c_table[0],
384
50.3k
              sizeof(helper->c_table),
385
50.3k
              char_c);
386
50.3k
    return TRUE;
387
50.3k
  }
388
389
4.39M
  while (index < number && index < NC) {
390
4.30M
    guint16 char_c = helper->pt_table[helper->bit_buf >> (BITBUFSIZ - 8)];
391
4.30M
    if (char_c >= NT) {
392
30.6k
      guint32 mask = 1U << (BITBUFSIZ - 1 - 8);
393
31.4k
      do {
394
31.4k
        if (mask & helper->bit_buf) {
395
12.6k
          char_c = helper->right[char_c];
396
18.8k
        } else {
397
18.8k
          char_c = helper->left[char_c];
398
18.8k
        }
399
31.4k
        mask >>= 1;
400
401
31.4k
      } while (char_c >= NT);
402
30.6k
    }
403
404
    /* advance what we have read */
405
4.30M
    if (!fu_efi_lz77_decompressor_read_source_bits(helper,
406
4.30M
                     helper->pt_len[char_c],
407
4.30M
                     error))
408
0
      return FALSE;
409
410
4.30M
    if (char_c <= 2) {
411
4.06M
      if (char_c == 0) {
412
4.01M
        char_c = 1;
413
4.01M
      } else if (char_c == 1) {
414
50.7k
        if (!fu_efi_lz77_decompressor_get_bits(helper, 4, &char_c, error))
415
0
          return FALSE;
416
50.7k
        char_c += 3;
417
50.7k
      } else if (char_c == 2) {
418
5.34k
        if (!fu_efi_lz77_decompressor_get_bits(helper,
419
5.34k
                       CBIT,
420
5.34k
                       &char_c,
421
5.34k
                       error))
422
0
          return FALSE;
423
5.34k
        char_c += 20;
424
5.34k
      }
425
8.53M
      while ((gint16)(--char_c) >= 0 && index < NC)
426
4.46M
        helper->c_len[index++] = 0;
427
4.06M
    } else {
428
236k
      helper->c_len[index++] = (guint8)(char_c - 2);
429
236k
    }
430
4.30M
  }
431
87.7k
  memset(helper->c_len + index, 0, sizeof(helper->c_len) - index);
432
87.7k
  return fu_efi_lz77_decompressor_make_huffman_table(helper,
433
87.7k
                 NC,
434
87.7k
                 helper->c_len,
435
87.7k
                 12,
436
87.7k
                 helper->c_table,
437
87.7k
                 error);
438
87.7k
}
439
440
/* get one code. if it is at block boundary, generate huffman code mapping table for extra set,
441
 * code&len set and position set */
442
static gboolean
443
fu_efi_lz77_decompressor_decode_c(FuEfiLz77DecompressHelper *helper, guint16 *value, GError **error)
444
522M
{
445
522M
  guint16 index2;
446
522M
  guint32 mask;
447
448
522M
  if (helper->block_size == 0) {
449
    /* starting a new block, so read blocksize from block header */
450
138k
    if (!fu_efi_lz77_decompressor_get_bits(helper, 16, &helper->block_size, error))
451
0
      return FALSE;
452
453
    /* read in the extra set code length array */
454
138k
    if (!fu_efi_lz77_decompressor_read_pt_len(helper, NT, TBIT, 3, error)) {
455
658
      g_prefix_error(
456
658
          error,
457
658
          "failed to generate the Huffman code mapping table for extra set: ");
458
658
      return FALSE;
459
658
    }
460
461
    /* read in and decode the char&len set code length array */
462
138k
    if (!fu_efi_lz77_decompressor_read_c_len(helper, error)) {
463
217
      g_prefix_error(error,
464
217
               "failed to generate the code mapping table for char&len: ");
465
217
      return FALSE;
466
217
    }
467
468
    /* read in the position set code length array */
469
137k
    if (!fu_efi_lz77_decompressor_read_pt_len(helper,
470
137k
                MAXNP,
471
137k
                helper->p_bit,
472
137k
                (guint16)(-1),
473
137k
                error)) {
474
901
      g_prefix_error(error,
475
901
               "failed to generate the Huffman code mapping table for the "
476
901
               "position set: ");
477
901
      return FALSE;
478
901
    }
479
137k
  }
480
481
  /* get one code according to code&set huffman table */
482
522M
  if (helper->block_size == 0) {
483
1.55k
    g_set_error_literal(error,
484
1.55k
            FWUPD_ERROR,
485
1.55k
            FWUPD_ERROR_INVALID_FILE,
486
1.55k
            "no blocks remained");
487
1.55k
    return FALSE;
488
1.55k
  }
489
522M
  helper->block_size--;
490
522M
  index2 = helper->c_table[helper->bit_buf >> (BITBUFSIZ - 12)];
491
522M
  if (index2 >= NC) {
492
32.6M
    mask = 1U << (BITBUFSIZ - 1 - 12);
493
34.0M
    do {
494
34.0M
      if ((helper->bit_buf & mask) != 0) {
495
4.20M
        index2 = helper->right[index2];
496
29.8M
      } else {
497
29.8M
        index2 = helper->left[index2];
498
29.8M
      }
499
34.0M
      mask >>= 1;
500
34.0M
    } while (index2 >= NC);
501
32.6M
  }
502
503
  /* advance what we have read */
504
522M
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, helper->c_len[index2], error))
505
0
    return FALSE;
506
522M
  *value = index2;
507
522M
  return TRUE;
508
522M
}
509
510
static gboolean
511
fu_efi_lz77_decompressor_internal(FuEfiLz77DecompressHelper *helper,
512
          FuEfiLz77DecompressorVersion version,
513
          GError **error)
514
5.23k
{
515
5.23k
  gsize dst_offset = 0;
516
517
  /* position set code length array size in the block header */
518
5.23k
  switch (version) {
519
2.83k
  case FU_EFI_LZ77_DECOMPRESSOR_VERSION_LEGACY:
520
2.83k
    helper->p_bit = 4;
521
2.83k
    break;
522
2.39k
  case FU_EFI_LZ77_DECOMPRESSOR_VERSION_TIANO:
523
2.39k
    helper->p_bit = 5;
524
2.39k
    break;
525
0
  default:
526
0
    g_set_error(error,
527
0
          FWUPD_ERROR,
528
0
          FWUPD_ERROR_INVALID_DATA,
529
0
          "unknown version 0x%x",
530
0
          version);
531
0
    return FALSE;
532
5.23k
  }
533
534
  /* fill the first BITBUFSIZ bits */
535
5.23k
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, BITBUFSIZ, error))
536
0
    return FALSE;
537
538
  /* decode each char */
539
522M
  while (dst_offset < helper->dst->len) {
540
522M
    guint16 char_c = 0;
541
542
    /* get one code */
543
522M
    if (!fu_efi_lz77_decompressor_decode_c(helper, &char_c, error))
544
3.32k
      return FALSE;
545
522M
    if (char_c < 256) {
546
      /* write original character into dst_buf */
547
514M
      helper->dst->data[dst_offset++] = (guint8)char_c;
548
514M
    } else {
549
8.22M
      guint16 bytes_remaining;
550
8.22M
      guint32 data_offset;
551
8.22M
      guint32 tmp = 0;
552
553
      /* process a pointer, so get string length */
554
8.22M
      bytes_remaining = (guint16)(char_c - (0x00000100U - THRESHOLD));
555
8.22M
      if (!fu_efi_lz77_decompressor_decode_p(helper, &tmp, error))
556
0
        return FALSE;
557
8.22M
      data_offset = dst_offset - tmp - 1;
558
559
      /* write bytes_remaining of bytes into dst_buf */
560
8.22M
      bytes_remaining--;
561
1.10G
      while ((gint16)(bytes_remaining) >= 0) {
562
1.09G
        if (dst_offset >= helper->dst->len) {
563
140
          g_set_error_literal(error,
564
140
                  FWUPD_ERROR,
565
140
                  FWUPD_ERROR_INVALID_DATA,
566
140
                  "bad pointer offset");
567
140
          return FALSE;
568
140
        }
569
1.09G
        if (data_offset >= helper->dst->len) {
570
221
          g_set_error_literal(error,
571
221
                  FWUPD_ERROR,
572
221
                  FWUPD_ERROR_INVALID_DATA,
573
221
                  "bad table");
574
221
          return FALSE;
575
221
        }
576
1.09G
        helper->dst->data[dst_offset++] = helper->dst->data[data_offset++];
577
1.09G
        bytes_remaining--;
578
1.09G
      }
579
8.22M
    }
580
522M
  }
581
582
  /* success */
583
1.54k
  return TRUE;
584
5.23k
}
585
586
static gboolean
587
fu_efi_lz77_decompressor_parse(FuFirmware *firmware,
588
             GInputStream *stream,
589
             FuFirmwareParseFlags flags,
590
             GError **error)
591
3.52k
{
592
3.52k
  gsize streamsz = 0;
593
3.52k
  guint32 dst_bufsz;
594
3.52k
  guint32 src_bufsz;
595
3.52k
  g_autoptr(GByteArray) st = NULL;
596
3.52k
  g_autoptr(GError) error_all = NULL;
597
3.52k
  g_autoptr(GByteArray) dst = g_byte_array_new();
598
3.52k
  FuEfiLz77DecompressorVersion decompressor_versions[] = {
599
3.52k
      FU_EFI_LZ77_DECOMPRESSOR_VERSION_LEGACY,
600
3.52k
      FU_EFI_LZ77_DECOMPRESSOR_VERSION_TIANO,
601
3.52k
  };
602
603
  /* parse header */
604
3.52k
  if (!fu_input_stream_size(stream, &streamsz, error))
605
0
    return FALSE;
606
3.52k
  st = fu_struct_efi_lz77_decompressor_header_parse_stream(stream, 0x0, error);
607
3.52k
  if (st == NULL)
608
33
    return FALSE;
609
3.49k
  src_bufsz = fu_struct_efi_lz77_decompressor_header_get_src_size(st);
610
3.49k
  if (streamsz < src_bufsz + st->len) {
611
107
    g_set_error_literal(error,
612
107
            FWUPD_ERROR,
613
107
            FWUPD_ERROR_INVALID_DATA,
614
107
            "source buffer is truncated");
615
107
    return FALSE;
616
107
  }
617
3.38k
  dst_bufsz = fu_struct_efi_lz77_decompressor_header_get_dst_size(st);
618
3.38k
  if (dst_bufsz == 0) {
619
24
    g_set_error_literal(error,
620
24
            FWUPD_ERROR,
621
24
            FWUPD_ERROR_INVALID_DATA,
622
24
            "destination size is zero");
623
24
    return FALSE;
624
24
  }
625
3.36k
  if (dst_bufsz > fu_firmware_get_size_max(firmware)) {
626
528
    g_autofree gchar *sz_val = g_format_size(dst_bufsz);
627
528
    g_autofree gchar *sz_max = g_format_size(fu_firmware_get_size_max(firmware));
628
528
    g_set_error(error,
629
528
          FWUPD_ERROR,
630
528
          FWUPD_ERROR_INVALID_DATA,
631
528
          "destination size is too large (%s, limit %s)",
632
528
          sz_val,
633
528
          sz_max);
634
528
    return FALSE;
635
528
  }
636
2.83k
  fu_byte_array_set_size(dst, dst_bufsz, 0x0);
637
638
  /* try both position */
639
6.52k
  for (guint i = 0; i < G_N_ELEMENTS(decompressor_versions); i++) {
640
5.23k
    FuEfiLz77DecompressHelper helper = {
641
5.23k
        .dst = dst,
642
5.23k
        .stream = stream,
643
5.23k
    };
644
5.23k
    g_autoptr(GError) error_local = NULL;
645
646
5.23k
    if (!g_seekable_seek(G_SEEKABLE(stream), st->len, G_SEEK_SET, NULL, error))
647
0
      return FALSE;
648
5.23k
    if (fu_efi_lz77_decompressor_internal(&helper,
649
5.23k
                  decompressor_versions[i],
650
5.23k
                  &error_local)) {
651
1.54k
      g_autoptr(GBytes) blob =
652
1.54k
          g_byte_array_free_to_bytes(g_steal_pointer(&dst)); /* nocheck:blocked */
653
1.54k
      if (!fu_firmware_set_stream(firmware, NULL, error))
654
0
        return FALSE;
655
1.54k
      fu_firmware_set_bytes(firmware, blob);
656
1.54k
      fu_firmware_set_version_raw(firmware, decompressor_versions[i]);
657
1.54k
      return TRUE;
658
1.54k
    }
659
3.68k
    if (error_all == NULL) {
660
2.39k
      g_propagate_prefixed_error(
661
2.39k
          &error_all,
662
2.39k
          g_steal_pointer(&error_local),
663
2.39k
          "failed to parse %s: ",
664
2.39k
          fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i]));
665
2.39k
      continue;
666
2.39k
    }
667
1.29k
    g_prefix_error(&error_all,
668
1.29k
             "failed to parse %s: %s: ",
669
1.29k
             fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i]),
670
1.29k
             error_local->message);
671
1.29k
  }
672
673
  /* success */
674
1.29k
  g_propagate_error(error, g_steal_pointer(&error_all));
675
1.29k
  return FALSE;
676
2.83k
}
677
678
static void
679
fu_efi_lz77_decompressor_init(FuEfiLz77Decompressor *self)
680
3.58k
{
681
3.58k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
682
3.58k
  fu_firmware_set_size_max(FU_FIRMWARE(self), 64 * 1024 * 1024);
683
3.58k
}
684
685
static void
686
fu_efi_lz77_decompressor_class_init(FuEfiLz77DecompressorClass *klass)
687
3
{
688
3
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
689
3
  firmware_class->parse = fu_efi_lz77_decompressor_parse;
690
3
}
691
692
/**
693
 * fu_efi_lz77_decompressor_new:
694
 *
695
 * Creates a new #FuFirmware that can be used to decompress LZ77.
696
 *
697
 * Since: 2.0.0
698
 **/
699
FuFirmware *
700
fu_efi_lz77_decompressor_new(void)
701
2.69k
{
702
2.69k
  return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_LZ77_DECOMPRESSOR, NULL));
703
2.69k
}