Coverage Report

Created: 2025-10-10 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-efi-lz77-decompressor.c
Line
Count
Source
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
3.27k
G_DEFINE_TYPE(FuEfiLz77Decompressor, fu_efi_lz77_decompressor, FU_TYPE_FIRMWARE)
34
3.27k
35
426M
#define BITBUFSIZ 32
36
417M
#define MAXMATCH  256
37
426M
#define THRESHOLD 3
38
3.07M
#define CODE_BIT  16
39
40
/* c: char&len set; p: position set; t: extra set */
41
417M
#define NC  (0xff + MAXMATCH + 2 - THRESHOLD)
42
123k
#define CBIT  9
43
10.5M
#define MAXPBIT 5
44
81.5k
#define TBIT  5
45
10.5M
#define MAXNP ((1U << MAXPBIT) - 1)
46
3.07M
#define NT  (CODE_BIT + 3)
47
#if NT > MAXNP
48
#define NPT NT
49
#else
50
893k
#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
163k
{
75
163k
  g_return_if_fail(length % 2 == 0);
76
163k
  length /= sizeof(guint16);
77
187M
  for (gsize i = 0; i < length; i++)
78
187M
    buf[i] = value;
79
163k
}
80
81
static gboolean
82
fu_efi_lz77_decompressor_read_source_bits(FuEfiLz77DecompressHelper *helper,
83
            guint16 number_of_bits,
84
            GError **error)
85
423M
{
86
  /* left shift number_of_bits of bits in advance */
87
423M
  helper->bit_buf = (guint32)(((guint64)helper->bit_buf) << number_of_bits);
88
89
  /* copy data needed in bytes into sub_bit_buf */
90
427M
  while (number_of_bits > helper->bit_count) {
91
3.66M
    gssize rc;
92
3.66M
    guint8 sub_bit_buf = 0;
93
94
3.66M
    number_of_bits = (guint16)(number_of_bits - helper->bit_count);
95
3.66M
    helper->bit_buf |= (guint32)(((guint64)helper->sub_bit_buf) << number_of_bits);
96
97
    /* get 1 byte into sub_bit_buf */
98
3.66M
    rc = g_input_stream_read(helper->stream,
99
3.66M
           &sub_bit_buf,
100
3.66M
           sizeof(sub_bit_buf),
101
3.66M
           NULL,
102
3.66M
           error);
103
3.66M
    if (rc < 0)
104
0
      return FALSE;
105
3.66M
    if (rc == 0) {
106
      /* no more bits from the source, just pad zero bit */
107
3.07M
      helper->sub_bit_buf = 0;
108
3.07M
    } else {
109
596k
      helper->sub_bit_buf = sub_bit_buf;
110
596k
    }
111
3.66M
    helper->bit_count = 8;
112
3.66M
  }
113
114
  /* calculate additional bit count read to update bit_count */
115
423M
  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
423M
  helper->bit_buf |= helper->sub_bit_buf >> helper->bit_count;
119
423M
  return TRUE;
120
423M
}
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
3.03M
{
128
  /* pop number_of_bits of bits from left */
129
3.03M
  *value = (guint16)(helper->bit_buf >> (BITBUFSIZ - number_of_bits));
130
131
  /* fill up bit_buf from source */
132
3.03M
  return fu_efi_lz77_decompressor_read_source_bits(helper, number_of_bits, error);
133
3.03M
}
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
79.2k
{
145
79.2k
  guint16 count[17] = {0};
146
79.2k
  guint16 weight[17] = {0};
147
79.2k
  guint16 start[18] = {0};
148
79.2k
  guint16 *pointer;
149
79.2k
  guint16 index;
150
79.2k
  guint16 c_char;
151
79.2k
  guint16 ju_bits;
152
79.2k
  guint16 avail_symbols;
153
79.2k
  guint16 mask;
154
79.2k
  guint16 max_table_length;
155
156
  /* the maximum mapping table width supported by this internal working function is 16 */
157
79.2k
  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
22.7M
  for (index = 0; index < number_of_symbols; index++) {
163
22.6M
    if (code_length_array[index] > 16) {
164
139
      g_set_error_literal(error,
165
139
              FWUPD_ERROR,
166
139
              FWUPD_ERROR_INVALID_DATA,
167
139
              "bad table");
168
139
      return FALSE;
169
139
    }
170
22.6M
    count[code_length_array[index]]++;
171
22.6M
  }
172
173
1.34M
  for (index = 1; index <= 16; index++) {
174
1.26M
    guint16 WordOfStart = start[index];
175
1.26M
    guint16 WordOfCount = count[index];
176
1.26M
    start[index + 1] = (guint16)(WordOfStart + (WordOfCount << (16 - index)));
177
1.26M
  }
178
179
79.1k
  if (start[17] != 0) {
180
    /*(1U << 16)*/
181
1.40k
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bad table");
182
1.40k
    return FALSE;
183
1.40k
  }
184
185
77.7k
  ju_bits = (guint16)(16 - mapping_table_bits);
186
869k
  for (index = 1; index <= mapping_table_bits; index++) {
187
791k
    start[index] >>= ju_bits;
188
791k
    weight[index] = (guint16)(1U << (mapping_table_bits - index));
189
791k
  }
190
191
529k
  while (index <= 16) {
192
452k
    weight[index] = (guint16)(1U << (16 - index));
193
452k
    index++;
194
452k
  }
195
196
77.7k
  index = (guint16)(start[mapping_table_bits + 1] >> ju_bits);
197
77.7k
  if (index != 0) {
198
730
    guint16 index3 = (guint16)(1U << mapping_table_bits);
199
730
    if (index < index3) {
200
730
      fu_efi_lz77_decompressor_memset16(table + index,
201
730
                (index3 - index) * sizeof(*table),
202
730
                0);
203
730
    }
204
730
  }
205
206
77.7k
  avail_symbols = number_of_symbols;
207
77.7k
  mask = (guint16)(1U << (15 - mapping_table_bits));
208
77.7k
  max_table_length = (guint16)(1U << mapping_table_bits);
209
210
22.5M
  for (c_char = 0; c_char < number_of_symbols; c_char++) {
211
22.4M
    guint16 len = code_length_array[c_char];
212
22.4M
    guint16 next_code;
213
214
22.4M
    if (len == 0 || len >= 17)
215
22.3M
      continue;
216
217
181k
    next_code = (guint16)(start[len] + weight[len]);
218
181k
    if (len <= mapping_table_bits) {
219
7.30M
      for (index = start[len]; index < next_code; index++) {
220
7.12M
        if (index >= max_table_length) {
221
116
          g_set_error_literal(error,
222
116
                  FWUPD_ERROR,
223
116
                  FWUPD_ERROR_INVALID_DATA,
224
116
                  "bad table");
225
116
          return FALSE;
226
116
        }
227
7.12M
        table[index] = c_char;
228
7.12M
      }
229
230
178k
    } else {
231
2.36k
      guint16 index3 = start[len];
232
2.36k
      pointer = &table[index3 >> ju_bits];
233
2.36k
      index = (guint16)(len - mapping_table_bits);
234
235
6.66k
      while (index != 0) {
236
4.30k
        if (*pointer == 0 && avail_symbols < (2 * NC - 1)) {
237
1.55k
          helper->right[avail_symbols] = helper->left[avail_symbols] =
238
1.55k
              0;
239
1.55k
          *pointer = avail_symbols++;
240
1.55k
        }
241
4.30k
        if (*pointer < (2 * NC - 1)) {
242
4.30k
          if ((index3 & mask) != 0)
243
2.24k
            pointer = &helper->right[*pointer];
244
2.06k
          else
245
2.06k
            pointer = &helper->left[*pointer];
246
4.30k
        }
247
4.30k
        index3 <<= 1;
248
4.30k
        index--;
249
4.30k
      }
250
2.36k
      *pointer = c_char;
251
2.36k
    }
252
181k
    start[len] = next_code;
253
181k
  }
254
  /* success */
255
77.6k
  return TRUE;
256
77.7k
}
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.50M
{
262
8.50M
  guint16 val;
263
264
8.50M
  val = helper->pt_table[helper->bit_buf >> (BITBUFSIZ - 8)];
265
8.50M
  if (val >= MAXNP) {
266
1.10M
    guint32 mask = 1U << (BITBUFSIZ - 1 - 8);
267
1.10M
    do {
268
1.10M
      if ((helper->bit_buf & mask) != 0) {
269
318k
        val = helper->right[val];
270
784k
      } else {
271
784k
        val = helper->left[val];
272
784k
      }
273
1.10M
      mask >>= 1;
274
1.10M
    } while (val >= MAXNP);
275
1.10M
  }
276
277
  /* advance what we have read */
278
8.50M
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, helper->pt_len[val], error))
279
0
    return FALSE;
280
281
8.50M
  if (val > 1) {
282
2.53M
    guint16 char_c = 0;
283
2.53M
    if (!fu_efi_lz77_decompressor_get_bits(helper, (guint16)(val - 1), &char_c, error))
284
0
      return FALSE;
285
2.53M
    *value = (guint32)((1U << (val - 1)) + char_c);
286
2.53M
    return TRUE;
287
2.53M
  }
288
5.96M
  *value = val;
289
5.96M
  return TRUE;
290
8.50M
}
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
162k
{
300
162k
  guint16 number = 0;
301
162k
  guint16 index = 0;
302
303
  /* read Extra Set Code Length Array size */
304
162k
  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
162k
  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
162k
  if (number == 0) {
313
    /* this represents only Huffman code used */
314
124k
    guint16 char_c = 0;
315
124k
    if (!fu_efi_lz77_decompressor_get_bits(helper, number_of_bits, &char_c, error))
316
0
      return FALSE;
317
124k
    fu_efi_lz77_decompressor_memset16(&helper->pt_table[0],
318
124k
              sizeof(helper->pt_table),
319
124k
              (guint16)char_c);
320
124k
    memset(helper->pt_len, 0, number_of_symbols);
321
124k
    return TRUE;
322
124k
  }
323
324
120k
  while (index < number && index < NPT) {
325
84.2k
    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
84.2k
    if (char_c == 7) {
331
5.10k
      guint32 mask = 1U << (BITBUFSIZ - 1 - 3);
332
24.4k
      while (mask & helper->bit_buf) {
333
19.3k
        mask >>= 1;
334
19.3k
        char_c += 1;
335
19.3k
      }
336
5.10k
    }
337
338
84.2k
    if (!fu_efi_lz77_decompressor_read_source_bits(
339
84.2k
      helper,
340
84.2k
      (guint16)((char_c < 7) ? 3 : char_c - 3),
341
84.2k
      error))
342
0
      return FALSE;
343
344
84.2k
    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
84.2k
    if (index == special_symbol) {
350
2.51k
      if (!fu_efi_lz77_decompressor_get_bits(helper, 2, &char_c, error))
351
0
        return FALSE;
352
2.51k
      if (char_c == 0) {
353
448
        g_set_error_literal(error,
354
448
                FWUPD_ERROR,
355
448
                FWUPD_ERROR_INVALID_DATA,
356
448
                "bad table");
357
448
        return FALSE;
358
448
      }
359
7.66k
      while ((gint16)(--char_c) >= 0 && index < NPT) {
360
5.59k
        helper->pt_len[index++] = 0;
361
5.59k
      }
362
2.07k
    }
363
84.2k
  }
364
840k
  while (index < number_of_symbols && index < NPT)
365
804k
    helper->pt_len[index++] = 0;
366
36.6k
  return fu_efi_lz77_decompressor_make_huffman_table(helper,
367
36.6k
                 number_of_symbols,
368
36.6k
                 helper->pt_len,
369
36.6k
                 8,
370
36.6k
                 helper->pt_table,
371
36.6k
                 error);
372
37.0k
}
373
374
/* read in and decode the Char&len Set Code Length Array, then generate the Huffman Code mapping
375
 * table for the char&len set */
376
static gboolean
377
fu_efi_lz77_decompressor_read_c_len(FuEfiLz77DecompressHelper *helper, GError **error)
378
80.6k
{
379
80.6k
  guint16 number = 0;
380
80.6k
  guint16 index = 0;
381
382
80.6k
  if (!fu_efi_lz77_decompressor_get_bits(helper, CBIT, &number, error))
383
0
    return FALSE;
384
80.6k
  if (number == 0) {
385
    /* this represents only Huffman code used */
386
38.0k
    guint16 char_c = 0;
387
38.0k
    if (!fu_efi_lz77_decompressor_get_bits(helper, CBIT, &char_c, error))
388
0
      return FALSE;
389
38.0k
    memset(helper->c_len, 0, sizeof(helper->c_len));
390
38.0k
    fu_efi_lz77_decompressor_memset16(&helper->c_table[0],
391
38.0k
              sizeof(helper->c_table),
392
38.0k
              char_c);
393
38.0k
    return TRUE;
394
38.0k
  }
395
396
2.98M
  while (index < number && index < NC) {
397
2.94M
    guint16 char_c = helper->pt_table[helper->bit_buf >> (BITBUFSIZ - 8)];
398
2.94M
    if (char_c >= NT) {
399
45.4k
      guint32 mask = 1U << (BITBUFSIZ - 1 - 8);
400
46.9k
      do {
401
46.9k
        if (mask & helper->bit_buf) {
402
27.1k
          char_c = helper->right[char_c];
403
27.1k
        } else {
404
19.7k
          char_c = helper->left[char_c];
405
19.7k
        }
406
46.9k
        mask >>= 1;
407
408
46.9k
      } while (char_c >= NT);
409
45.4k
    }
410
411
    /* advance what we have read */
412
2.94M
    if (!fu_efi_lz77_decompressor_read_source_bits(helper,
413
2.94M
                     helper->pt_len[char_c],
414
2.94M
                     error))
415
0
      return FALSE;
416
417
2.94M
    if (char_c <= 2) {
418
2.72M
      if (char_c == 0) {
419
2.70M
        char_c = 1;
420
2.70M
      } else if (char_c == 1) {
421
7.56k
        if (!fu_efi_lz77_decompressor_get_bits(helper, 4, &char_c, error))
422
0
          return FALSE;
423
7.56k
        char_c += 3;
424
7.56k
      } else if (char_c == 2) {
425
4.65k
        if (!fu_efi_lz77_decompressor_get_bits(helper,
426
4.65k
                       CBIT,
427
4.65k
                       &char_c,
428
4.65k
                       error))
429
0
          return FALSE;
430
4.65k
        char_c += 20;
431
4.65k
      }
432
2.72M
      if (char_c == 0) {
433
0
        g_set_error_literal(error,
434
0
                FWUPD_ERROR,
435
0
                FWUPD_ERROR_INVALID_DATA,
436
0
                "bad table");
437
0
        return FALSE;
438
0
      }
439
6.26M
      while ((gint16)(--char_c) >= 0 && index < NC)
440
3.54M
        helper->c_len[index++] = 0;
441
2.72M
    } else {
442
225k
      helper->c_len[index++] = (guint8)(char_c - 2);
443
225k
    }
444
2.94M
  }
445
42.6k
  memset(helper->c_len + index, 0, sizeof(helper->c_len) - index);
446
42.6k
  return fu_efi_lz77_decompressor_make_huffman_table(helper,
447
42.6k
                 NC,
448
42.6k
                 helper->c_len,
449
42.6k
                 12,
450
42.6k
                 helper->c_table,
451
42.6k
                 error);
452
42.6k
}
453
454
/* get one code. if it is at block boundary, generate huffman code mapping table for extra set,
455
 * code&len set and position set */
456
static gboolean
457
fu_efi_lz77_decompressor_decode_c(FuEfiLz77DecompressHelper *helper, guint16 *value, GError **error)
458
409M
{
459
409M
  guint16 index2;
460
409M
  guint32 mask;
461
462
409M
  if (helper->block_size == 0) {
463
    /* starting a new block, so read blocksize from block header */
464
81.5k
    if (!fu_efi_lz77_decompressor_get_bits(helper, 16, &helper->block_size, error))
465
0
      return FALSE;
466
467
    /* read in the extra set code length array */
468
81.5k
    if (!fu_efi_lz77_decompressor_read_pt_len(helper, NT, TBIT, 3, error)) {
469
863
      g_prefix_error_literal(
470
863
          error,
471
863
          "failed to generate the Huffman code mapping table for extra set: ");
472
863
      return FALSE;
473
863
    }
474
475
    /* read in and decode the char&len set code length array */
476
80.6k
    if (!fu_efi_lz77_decompressor_read_c_len(helper, error)) {
477
229
      g_prefix_error_literal(
478
229
          error,
479
229
          "failed to generate the code mapping table for char&len: ");
480
229
      return FALSE;
481
229
    }
482
483
    /* read in the position set code length array */
484
80.4k
    if (!fu_efi_lz77_decompressor_read_pt_len(helper,
485
80.4k
                MAXNP,
486
80.4k
                helper->p_bit,
487
80.4k
                (guint16)(-1),
488
80.4k
                error)) {
489
1.01k
      g_prefix_error_literal(
490
1.01k
          error,
491
1.01k
          "failed to generate the Huffman code mapping table for the "
492
1.01k
          "position set: ");
493
1.01k
      return FALSE;
494
1.01k
    }
495
80.4k
  }
496
497
  /* get one code according to code&set huffman table */
498
409M
  if (helper->block_size == 0) {
499
1.22k
    g_set_error_literal(error,
500
1.22k
            FWUPD_ERROR,
501
1.22k
            FWUPD_ERROR_INVALID_FILE,
502
1.22k
            "no blocks remained");
503
1.22k
    return FALSE;
504
1.22k
  }
505
409M
  helper->block_size--;
506
409M
  index2 = helper->c_table[helper->bit_buf >> (BITBUFSIZ - 12)];
507
409M
  if (index2 >= NC) {
508
1.67M
    mask = 1U << (BITBUFSIZ - 1 - 12);
509
1.67M
    do {
510
1.67M
      if ((helper->bit_buf & mask) != 0) {
511
331k
        index2 = helper->right[index2];
512
1.33M
      } else {
513
1.33M
        index2 = helper->left[index2];
514
1.33M
      }
515
1.67M
      mask >>= 1;
516
1.67M
    } while (index2 >= NC);
517
1.67M
  }
518
519
  /* advance what we have read */
520
409M
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, helper->c_len[index2], error))
521
0
    return FALSE;
522
409M
  *value = index2;
523
409M
  return TRUE;
524
409M
}
525
526
static gboolean
527
fu_efi_lz77_decompressor_internal(FuEfiLz77DecompressHelper *helper,
528
          FuEfiLz77DecompressorVersion version,
529
          GError **error)
530
4.89k
{
531
4.89k
  gsize dst_offset = 0;
532
533
  /* position set code length array size in the block header */
534
4.89k
  switch (version) {
535
2.64k
  case FU_EFI_LZ77_DECOMPRESSOR_VERSION_LEGACY:
536
2.64k
    helper->p_bit = 4;
537
2.64k
    break;
538
2.25k
  case FU_EFI_LZ77_DECOMPRESSOR_VERSION_TIANO:
539
2.25k
    helper->p_bit = 5;
540
2.25k
    break;
541
0
  default:
542
0
    g_set_error(error,
543
0
          FWUPD_ERROR,
544
0
          FWUPD_ERROR_INVALID_DATA,
545
0
          "unknown version 0x%x",
546
0
          version);
547
0
    return FALSE;
548
4.89k
  }
549
550
  /* fill the first BITBUFSIZ bits */
551
4.89k
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, BITBUFSIZ, error))
552
0
    return FALSE;
553
554
  /* decode each char */
555
409M
  while (dst_offset < helper->dst->len) {
556
409M
    guint16 char_c = 0;
557
558
    /* get one code */
559
409M
    if (!fu_efi_lz77_decompressor_decode_c(helper, &char_c, error))
560
3.32k
      return FALSE;
561
409M
    if (char_c < 256) {
562
      /* write original character into dst_buf */
563
400M
      helper->dst->data[dst_offset++] = (guint8)char_c;
564
400M
    } else {
565
8.50M
      guint16 bytes_remaining;
566
8.50M
      guint32 data_offset;
567
8.50M
      guint32 tmp = 0;
568
569
      /* process a pointer, so get string length */
570
8.50M
      bytes_remaining = (guint16)(char_c - (0x00000100U - THRESHOLD));
571
8.50M
      if (!fu_efi_lz77_decompressor_decode_p(helper, &tmp, error))
572
0
        return FALSE;
573
8.50M
      data_offset = dst_offset - tmp - 1;
574
575
      /* write bytes_remaining of bytes into dst_buf */
576
8.50M
      bytes_remaining--;
577
1.28G
      while ((gint16)(bytes_remaining) >= 0) {
578
1.27G
        if (dst_offset >= helper->dst->len) {
579
123
          g_set_error_literal(error,
580
123
                  FWUPD_ERROR,
581
123
                  FWUPD_ERROR_INVALID_DATA,
582
123
                  "bad pointer offset");
583
123
          return FALSE;
584
123
        }
585
1.27G
        if (data_offset >= helper->dst->len) {
586
202
          g_set_error_literal(error,
587
202
                  FWUPD_ERROR,
588
202
                  FWUPD_ERROR_INVALID_DATA,
589
202
                  "bad table");
590
202
          return FALSE;
591
202
        }
592
1.27G
        helper->dst->data[dst_offset++] = helper->dst->data[data_offset++];
593
1.27G
        bytes_remaining--;
594
1.27G
      }
595
8.50M
    }
596
409M
  }
597
598
  /* success */
599
1.24k
  return TRUE;
600
4.89k
}
601
602
static gboolean
603
fu_efi_lz77_decompressor_parse(FuFirmware *firmware,
604
             GInputStream *stream,
605
             FuFirmwareParseFlags flags,
606
             GError **error)
607
3.22k
{
608
3.22k
  gsize streamsz = 0;
609
3.22k
  guint32 dst_bufsz;
610
3.22k
  guint32 src_bufsz;
611
3.22k
  g_autoptr(FuStructEfiLz77DecompressorHeader) st = NULL;
612
3.22k
  g_autoptr(GError) error_all = NULL;
613
3.22k
  g_autoptr(GByteArray) dst = g_byte_array_new();
614
3.22k
  FuEfiLz77DecompressorVersion decompressor_versions[] = {
615
3.22k
      FU_EFI_LZ77_DECOMPRESSOR_VERSION_LEGACY,
616
3.22k
      FU_EFI_LZ77_DECOMPRESSOR_VERSION_TIANO,
617
3.22k
  };
618
619
  /* parse header */
620
3.22k
  if (!fu_input_stream_size(stream, &streamsz, error))
621
0
    return FALSE;
622
3.22k
  st = fu_struct_efi_lz77_decompressor_header_parse_stream(stream, 0x0, error);
623
3.22k
  if (st == NULL)
624
26
    return FALSE;
625
3.19k
  src_bufsz = fu_struct_efi_lz77_decompressor_header_get_src_size(st);
626
3.19k
  if (streamsz < src_bufsz + st->len) {
627
84
    g_set_error_literal(error,
628
84
            FWUPD_ERROR,
629
84
            FWUPD_ERROR_INVALID_DATA,
630
84
            "source buffer is truncated");
631
84
    return FALSE;
632
84
  }
633
3.11k
  dst_bufsz = fu_struct_efi_lz77_decompressor_header_get_dst_size(st);
634
3.11k
  if (dst_bufsz == 0) {
635
19
    g_set_error_literal(error,
636
19
            FWUPD_ERROR,
637
19
            FWUPD_ERROR_INVALID_DATA,
638
19
            "destination size is zero");
639
19
    return FALSE;
640
19
  }
641
3.09k
  if (dst_bufsz > fu_firmware_get_size_max(firmware)) {
642
451
    g_autofree gchar *sz_val = g_format_size(dst_bufsz);
643
451
    g_autofree gchar *sz_max = g_format_size(fu_firmware_get_size_max(firmware));
644
451
    g_set_error(error,
645
451
          FWUPD_ERROR,
646
451
          FWUPD_ERROR_INVALID_DATA,
647
451
          "destination size is too large (%s, limit %s)",
648
451
          sz_val,
649
451
          sz_max);
650
451
    return FALSE;
651
451
  }
652
2.64k
  fu_byte_array_set_size(dst, dst_bufsz, 0x0);
653
654
  /* try both position */
655
6.29k
  for (guint i = 0; i < G_N_ELEMENTS(decompressor_versions); i++) {
656
4.89k
    FuEfiLz77DecompressHelper helper = {
657
4.89k
        .dst = dst,
658
4.89k
        .stream = stream,
659
4.89k
    };
660
4.89k
    g_autoptr(GError) error_local = NULL;
661
662
4.89k
    if (!g_seekable_seek(G_SEEKABLE(stream), st->len, G_SEEK_SET, NULL, error))
663
0
      return FALSE;
664
4.89k
    if (fu_efi_lz77_decompressor_internal(&helper,
665
4.89k
                  decompressor_versions[i],
666
4.89k
                  &error_local)) {
667
1.24k
      g_autoptr(GBytes) blob =
668
1.24k
          g_byte_array_free_to_bytes(g_steal_pointer(&dst)); /* nocheck:blocked */
669
1.24k
      if (!fu_firmware_set_stream(firmware, NULL, error))
670
0
        return FALSE;
671
1.24k
      fu_firmware_set_bytes(firmware, blob);
672
1.24k
      fu_firmware_set_version_raw(firmware, decompressor_versions[i]);
673
1.24k
      return TRUE;
674
1.24k
    }
675
3.65k
    if (error_all == NULL) {
676
2.25k
      g_propagate_prefixed_error(
677
2.25k
          &error_all,
678
2.25k
          g_steal_pointer(&error_local),
679
2.25k
          "failed to parse %s: ",
680
2.25k
          fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i]));
681
2.25k
      continue;
682
2.25k
    }
683
1.39k
    g_prefix_error(&error_all, /* nocheck:error */
684
1.39k
             "failed to parse %s: %s: ",
685
1.39k
             fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i]),
686
1.39k
             error_local->message);
687
1.39k
  }
688
689
  /* success */
690
1.39k
  g_propagate_error(error, g_steal_pointer(&error_all));
691
1.39k
  return FALSE;
692
2.64k
}
693
694
static void
695
fu_efi_lz77_decompressor_init(FuEfiLz77Decompressor *self)
696
3.27k
{
697
3.27k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
698
3.27k
  fu_firmware_set_size_max(FU_FIRMWARE(self), 64 * 1024 * 1024);
699
3.27k
}
700
701
static void
702
fu_efi_lz77_decompressor_class_init(FuEfiLz77DecompressorClass *klass)
703
3
{
704
3
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
705
3
  firmware_class->parse = fu_efi_lz77_decompressor_parse;
706
3
}
707
708
/**
709
 * fu_efi_lz77_decompressor_new:
710
 *
711
 * Creates a new #FuFirmware that can be used to decompress LZ77.
712
 *
713
 * Since: 2.0.0
714
 **/
715
FuFirmware *
716
fu_efi_lz77_decompressor_new(void)
717
2.44k
{
718
2.44k
  return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_LZ77_DECOMPRESSOR, NULL));
719
2.44k
}