Coverage Report

Created: 2026-01-17 07:04

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
2.67k
G_DEFINE_TYPE(FuEfiLz77Decompressor, fu_efi_lz77_decompressor, FU_TYPE_FIRMWARE)
34
2.67k
35
411M
#define BITBUFSIZ 32
36
403M
#define MAXMATCH  256
37
411M
#define THRESHOLD 3
38
2.49M
#define CODE_BIT  16
39
40
/* c: char&len set; p: position set; t: extra set */
41
403M
#define NC  (0xff + MAXMATCH + 2 - THRESHOLD)
42
177k
#define CBIT  9
43
10.7M
#define MAXPBIT 5
44
117k
#define TBIT  5
45
10.7M
#define MAXNP ((1U << MAXPBIT) - 1)
46
2.49M
#define NT  (CODE_BIT + 3)
47
#if NT > MAXNP
48
#define NPT NT
49
#else
50
2.04M
#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
210k
{
75
210k
  g_return_if_fail(length % 2 == 0);
76
210k
  length /= sizeof(guint16);
77
270M
  for (gsize i = 0; i < length; i++)
78
270M
    buf[i] = value;
79
210k
}
80
81
static gboolean
82
fu_efi_lz77_decompressor_read_source_bits(FuEfiLz77DecompressHelper *helper,
83
            guint16 number_of_bits,
84
            GError **error)
85
410M
{
86
  /* left shift number_of_bits of bits in advance */
87
410M
  helper->bit_buf = (guint32)(((guint64)helper->bit_buf) << number_of_bits);
88
89
  /* copy data needed in bytes into sub_bit_buf */
90
414M
  while (number_of_bits > helper->bit_count) {
91
4.07M
    gssize rc;
92
4.07M
    guint8 sub_bit_buf = 0;
93
94
4.07M
    number_of_bits = (guint16)(number_of_bits - helper->bit_count);
95
4.07M
    helper->bit_buf |= (guint32)(((guint64)helper->sub_bit_buf) << number_of_bits);
96
97
    /* get 1 byte into sub_bit_buf */
98
4.07M
    rc = g_input_stream_read(helper->stream,
99
4.07M
           &sub_bit_buf,
100
4.07M
           sizeof(sub_bit_buf),
101
4.07M
           NULL,
102
4.07M
           error);
103
4.07M
    if (rc < 0)
104
0
      return FALSE;
105
4.07M
    if (rc == 0) {
106
      /* no more bits from the source, just pad zero bit */
107
2.94M
      helper->sub_bit_buf = 0;
108
2.94M
    } else {
109
1.13M
      helper->sub_bit_buf = sub_bit_buf;
110
1.13M
    }
111
4.07M
    helper->bit_count = 8;
112
4.07M
  }
113
114
  /* calculate additional bit count read to update bit_count */
115
410M
  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
410M
  helper->bit_buf |= helper->sub_bit_buf >> helper->bit_count;
119
410M
  return TRUE;
120
410M
}
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.59M
{
128
  /* pop number_of_bits of bits from left */
129
2.59M
  *value = (guint16)(helper->bit_buf >> (BITBUFSIZ - number_of_bits));
130
131
  /* fill up bit_buf from source */
132
2.59M
  return fu_efi_lz77_decompressor_read_source_bits(helper, number_of_bits, error);
133
2.59M
}
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
140k
{
145
140k
  guint16 count[17] = {0};
146
140k
  guint16 weight[17] = {0};
147
140k
  guint16 start[18] = {0};
148
140k
  guint16 *pointer;
149
140k
  guint16 index;
150
140k
  guint16 c_char;
151
140k
  guint16 ju_bits;
152
140k
  guint16 avail_symbols;
153
140k
  guint16 mask;
154
140k
  guint16 max_table_length;
155
156
  /* the maximum mapping table width supported by this internal working function is 16 */
157
140k
  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
33.0M
  for (index = 0; index < number_of_symbols; index++) {
163
32.9M
    if (code_length_array[index] > 16) {
164
89
      g_set_error_literal(error,
165
89
              FWUPD_ERROR,
166
89
              FWUPD_ERROR_INVALID_DATA,
167
89
              "bad table");
168
89
      return FALSE;
169
89
    }
170
32.9M
    count[code_length_array[index]]++;
171
32.9M
  }
172
173
2.39M
  for (index = 1; index <= 16; index++) {
174
2.25M
    guint16 word_of_start = start[index];
175
2.25M
    guint16 word_of_count = count[index];
176
2.25M
    start[index + 1] = (guint16)(word_of_start + (word_of_count << (16 - index)));
177
2.25M
  }
178
179
140k
  if (start[17] != 0) {
180
    /*(1U << 16)*/
181
990
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bad table");
182
990
    return FALSE;
183
990
  }
184
185
139k
  ju_bits = (guint16)(16 - mapping_table_bits);
186
1.50M
  for (index = 1; index <= mapping_table_bits; index++) {
187
1.36M
    start[index] >>= ju_bits;
188
1.36M
    weight[index] = (guint16)(1U << (mapping_table_bits - index));
189
1.36M
  }
190
191
1.01M
  while (index <= 16) {
192
877k
    weight[index] = (guint16)(1U << (16 - index));
193
877k
    index++;
194
877k
  }
195
196
139k
  index = (guint16)(start[mapping_table_bits + 1] >> ju_bits);
197
139k
  if (index != 0) {
198
754
    guint16 index3 = (guint16)(1U << mapping_table_bits);
199
754
    if (index < index3) {
200
754
      fu_efi_lz77_decompressor_memset16(table + index,
201
754
                (index3 - index) * sizeof(*table),
202
754
                0);
203
754
    }
204
754
  }
205
206
139k
  avail_symbols = number_of_symbols;
207
139k
  mask = (guint16)(1U << (15 - mapping_table_bits));
208
139k
  max_table_length = (guint16)(1U << mapping_table_bits);
209
210
32.9M
  for (c_char = 0; c_char < number_of_symbols; c_char++) {
211
32.7M
    guint16 len = code_length_array[c_char];
212
32.7M
    guint16 next_code;
213
214
32.7M
    if (len == 0 || len >= 17)
215
32.5M
      continue;
216
217
205k
    next_code = (guint16)(start[len] + weight[len]);
218
205k
    if (len <= mapping_table_bits) {
219
9.13M
      for (index = start[len]; index < next_code; index++) {
220
8.93M
        if (index >= max_table_length) {
221
124
          g_set_error_literal(error,
222
124
                  FWUPD_ERROR,
223
124
                  FWUPD_ERROR_INVALID_DATA,
224
124
                  "bad table");
225
124
          return FALSE;
226
124
        }
227
8.93M
        table[index] = c_char;
228
8.93M
      }
229
230
202k
    } else {
231
2.99k
      guint16 index3 = start[len];
232
2.99k
      pointer = &table[index3 >> ju_bits];
233
2.99k
      index = (guint16)(len - mapping_table_bits);
234
235
8.70k
      while (index != 0) {
236
5.71k
        if (*pointer == 0 && avail_symbols < (2 * NC - 1)) {
237
1.99k
          helper->right[avail_symbols] = helper->left[avail_symbols] =
238
1.99k
              0;
239
1.99k
          *pointer = avail_symbols++;
240
1.99k
        }
241
5.71k
        if (*pointer < (2 * NC - 1)) {
242
5.71k
          if ((index3 & mask) != 0)
243
3.04k
            pointer = &helper->right[*pointer];
244
2.66k
          else
245
2.66k
            pointer = &helper->left[*pointer];
246
5.71k
        }
247
5.71k
        index3 <<= 1;
248
5.71k
        index--;
249
5.71k
      }
250
2.99k
      *pointer = c_char;
251
2.99k
    }
252
205k
    start[len] = next_code;
253
205k
  }
254
  /* success */
255
139k
  return TRUE;
256
139k
}
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.05M
{
262
8.05M
  guint16 val;
263
264
8.05M
  val = helper->pt_table[helper->bit_buf >> (BITBUFSIZ - 8)];
265
8.05M
  if (val >= MAXNP) {
266
497k
    guint32 mask = 1U << (BITBUFSIZ - 1 - 8);
267
497k
    do {
268
497k
      if ((helper->bit_buf & mask) != 0) {
269
167k
        val = helper->right[val];
270
330k
      } else {
271
330k
        val = helper->left[val];
272
330k
      }
273
497k
      mask >>= 1;
274
497k
    } while (val >= MAXNP);
275
497k
  }
276
277
  /* advance what we have read */
278
8.05M
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, helper->pt_len[val], error))
279
0
    return FALSE;
280
281
8.05M
  if (val > 1) {
282
1.90M
    guint16 char_c = 0;
283
1.90M
    if (!fu_efi_lz77_decompressor_get_bits(helper, (guint16)(val - 1), &char_c, error))
284
0
      return FALSE;
285
1.90M
    *value = (guint32)((1U << (val - 1)) + char_c);
286
1.90M
    return TRUE;
287
1.90M
  }
288
6.15M
  *value = val;
289
6.15M
  return TRUE;
290
8.05M
}
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
234k
{
300
234k
  guint16 number = 0;
301
234k
  guint16 index = 0;
302
303
  /* read Extra Set Code Length Array size */
304
234k
  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
234k
  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
234k
  if (number == 0) {
313
    /* this represents only Huffman code used */
314
153k
    guint16 char_c = 0;
315
153k
    if (!fu_efi_lz77_decompressor_get_bits(helper, number_of_bits, &char_c, error))
316
0
      return FALSE;
317
153k
    fu_efi_lz77_decompressor_memset16(&helper->pt_table[0],
318
153k
              sizeof(helper->pt_table),
319
153k
              (guint16)char_c);
320
153k
    memset(helper->pt_len, 0, number_of_symbols);
321
153k
    return TRUE;
322
153k
  }
323
324
304k
  while (index < number && index < NPT) {
325
223k
    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
223k
    if (char_c == 7) {
331
4.37k
      guint32 mask = 1U << (BITBUFSIZ - 1 - 3);
332
17.4k
      while (mask & helper->bit_buf) {
333
13.0k
        mask >>= 1;
334
13.0k
        char_c += 1;
335
13.0k
      }
336
4.37k
    }
337
338
223k
    if (!fu_efi_lz77_decompressor_read_source_bits(
339
223k
      helper,
340
223k
      (guint16)((char_c < 7) ? 3 : char_c - 3),
341
223k
      error))
342
0
      return FALSE;
343
344
223k
    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
223k
    if (index == special_symbol) {
350
2.32k
      if (!fu_efi_lz77_decompressor_get_bits(helper, 2, &char_c, error))
351
0
        return FALSE;
352
2.32k
      if (char_c == 0) {
353
357
        g_set_error_literal(error,
354
357
                FWUPD_ERROR,
355
357
                FWUPD_ERROR_INVALID_DATA,
356
357
                "bad table");
357
357
        return FALSE;
358
357
      }
359
7.50k
      while ((gint16)(--char_c) >= 0 && index < NPT) {
360
5.53k
        helper->pt_len[index++] = 0;
361
5.53k
      }
362
1.97k
    }
363
223k
  }
364
1.90M
  while (index < number_of_symbols && index < NPT)
365
1.81M
    helper->pt_len[index++] = 0;
366
80.4k
  return fu_efi_lz77_decompressor_make_huffman_table(helper,
367
80.4k
                 number_of_symbols,
368
80.4k
                 helper->pt_len,
369
80.4k
                 8,
370
80.4k
                 helper->pt_table,
371
80.4k
                 error);
372
80.7k
}
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
116k
{
379
116k
  guint16 number = 0;
380
116k
  guint16 index = 0;
381
382
116k
  if (!fu_efi_lz77_decompressor_get_bits(helper, CBIT, &number, error))
383
0
    return FALSE;
384
116k
  if (number == 0) {
385
    /* this represents only Huffman code used */
386
56.3k
    guint16 char_c = 0;
387
56.3k
    if (!fu_efi_lz77_decompressor_get_bits(helper, CBIT, &char_c, error))
388
0
      return FALSE;
389
56.3k
    memset(helper->c_len, 0, sizeof(helper->c_len));
390
56.3k
    fu_efi_lz77_decompressor_memset16(&helper->c_table[0],
391
56.3k
              sizeof(helper->c_table),
392
56.3k
              char_c);
393
56.3k
    return TRUE;
394
56.3k
  }
395
396
2.40M
  while (index < number && index < NC) {
397
2.34M
    guint16 char_c = helper->pt_table[helper->bit_buf >> (BITBUFSIZ - 8)];
398
2.34M
    if (char_c >= NT) {
399
29.9k
      guint32 mask = 1U << (BITBUFSIZ - 1 - 8);
400
30.4k
      do {
401
30.4k
        if (mask & helper->bit_buf) {
402
20.0k
          char_c = helper->right[char_c];
403
20.0k
        } else {
404
10.3k
          char_c = helper->left[char_c];
405
10.3k
        }
406
30.4k
        mask >>= 1;
407
408
30.4k
      } while (char_c >= NT);
409
29.9k
    }
410
411
    /* advance what we have read */
412
2.34M
    if (!fu_efi_lz77_decompressor_read_source_bits(helper,
413
2.34M
                     helper->pt_len[char_c],
414
2.34M
                     error))
415
0
      return FALSE;
416
417
2.34M
    if (char_c <= 2) {
418
2.11M
      if (char_c == 0) {
419
2.10M
        char_c = 1;
420
2.10M
      } else if (char_c == 1) {
421
6.47k
        if (!fu_efi_lz77_decompressor_get_bits(helper, 4, &char_c, error))
422
0
          return FALSE;
423
6.47k
        char_c += 3;
424
6.47k
      } else if (char_c == 2) {
425
4.19k
        if (!fu_efi_lz77_decompressor_get_bits(helper,
426
4.19k
                       CBIT,
427
4.19k
                       &char_c,
428
4.19k
                       error))
429
0
          return FALSE;
430
4.19k
        char_c += 20;
431
4.19k
      }
432
2.11M
      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
5.02M
      while ((gint16)(--char_c) >= 0 && index < NC)
440
2.90M
        helper->c_len[index++] = 0;
441
2.11M
    } else {
442
226k
      helper->c_len[index++] = (guint8)(char_c - 2);
443
226k
    }
444
2.34M
  }
445
60.5k
  memset(helper->c_len + index, 0, sizeof(helper->c_len) - index);
446
60.5k
  return fu_efi_lz77_decompressor_make_huffman_table(helper,
447
60.5k
                 NC,
448
60.5k
                 helper->c_len,
449
60.5k
                 12,
450
60.5k
                 helper->c_table,
451
60.5k
                 error);
452
60.5k
}
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
396M
{
459
396M
  guint16 index2;
460
396M
  guint32 mask;
461
462
396M
  if (helper->block_size == 0) {
463
    /* starting a new block, so read blocksize from block header */
464
117k
    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
117k
    if (!fu_efi_lz77_decompressor_read_pt_len(helper, NT, TBIT, 3, error)) {
469
585
      g_prefix_error_literal(
470
585
          error,
471
585
          "failed to generate the Huffman code mapping table for extra set: ");
472
585
      return FALSE;
473
585
    }
474
475
    /* read in and decode the char&len set code length array */
476
116k
    if (!fu_efi_lz77_decompressor_read_c_len(helper, error)) {
477
170
      g_prefix_error_literal(
478
170
          error,
479
170
          "failed to generate the code mapping table for char&len: ");
480
170
      return FALSE;
481
170
    }
482
483
    /* read in the position set code length array */
484
116k
    if (!fu_efi_lz77_decompressor_read_pt_len(helper,
485
116k
                MAXNP,
486
116k
                helper->p_bit,
487
116k
                (guint16)(-1),
488
116k
                error)) {
489
805
      g_prefix_error_literal(
490
805
          error,
491
805
          "failed to generate the Huffman code mapping table for the "
492
805
          "position set: ");
493
805
      return FALSE;
494
805
    }
495
116k
  }
496
497
  /* get one code according to code&set huffman table */
498
396M
  if (helper->block_size == 0) {
499
1.04k
    g_set_error_literal(error,
500
1.04k
            FWUPD_ERROR,
501
1.04k
            FWUPD_ERROR_INVALID_FILE,
502
1.04k
            "no blocks remained");
503
1.04k
    return FALSE;
504
1.04k
  }
505
396M
  helper->block_size--;
506
396M
  index2 = helper->c_table[helper->bit_buf >> (BITBUFSIZ - 12)];
507
396M
  if (index2 >= NC) {
508
869k
    mask = 1U << (BITBUFSIZ - 1 - 12);
509
929k
    do {
510
929k
      if ((helper->bit_buf & mask) != 0) {
511
207k
        index2 = helper->right[index2];
512
722k
      } else {
513
722k
        index2 = helper->left[index2];
514
722k
      }
515
929k
      mask >>= 1;
516
929k
    } while (index2 >= NC);
517
869k
  }
518
519
  /* advance what we have read */
520
396M
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, helper->c_len[index2], error))
521
0
    return FALSE;
522
396M
  *value = index2;
523
396M
  return TRUE;
524
396M
}
525
526
static gboolean
527
fu_efi_lz77_decompressor_internal(FuEfiLz77DecompressHelper *helper,
528
          FuEfiLz77DecompressorVersion version,
529
          GError **error)
530
4.13k
{
531
4.13k
  gsize dst_offset = 0;
532
533
  /* position set code length array size in the block header */
534
4.13k
  switch (version) {
535
2.26k
  case FU_EFI_LZ77_DECOMPRESSOR_VERSION_LEGACY:
536
2.26k
    helper->p_bit = 4;
537
2.26k
    break;
538
1.86k
  case FU_EFI_LZ77_DECOMPRESSOR_VERSION_TIANO:
539
1.86k
    helper->p_bit = 5;
540
1.86k
    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.13k
  }
549
550
  /* fill the first BITBUFSIZ bits */
551
4.13k
  if (!fu_efi_lz77_decompressor_read_source_bits(helper, BITBUFSIZ, error))
552
0
    return FALSE;
553
554
  /* decode each char */
555
396M
  while (dst_offset < helper->dst->len) {
556
396M
    guint16 char_c = 0;
557
558
    /* get one code */
559
396M
    if (!fu_efi_lz77_decompressor_decode_c(helper, &char_c, error))
560
2.60k
      return FALSE;
561
396M
    if (char_c < 256) {
562
      /* write original character into dst_buf */
563
388M
      helper->dst->data[dst_offset++] = (guint8)char_c;
564
388M
    } else {
565
8.05M
      guint16 bytes_remaining;
566
8.05M
      guint32 data_offset;
567
8.05M
      guint32 tmp = 0;
568
569
      /* process a pointer, so get string length */
570
8.05M
      bytes_remaining = (guint16)(char_c - (0x00000100U - THRESHOLD));
571
8.05M
      if (!fu_efi_lz77_decompressor_decode_p(helper, &tmp, error))
572
0
        return FALSE;
573
8.05M
      data_offset = dst_offset - tmp - 1;
574
575
      /* write bytes_remaining of bytes into dst_buf */
576
8.05M
      bytes_remaining--;
577
1.13G
      while ((gint16)(bytes_remaining) >= 0) {
578
1.12G
        if (dst_offset >= helper->dst->len) {
579
86
          g_set_error_literal(error,
580
86
                  FWUPD_ERROR,
581
86
                  FWUPD_ERROR_INVALID_DATA,
582
86
                  "bad pointer offset");
583
86
          return FALSE;
584
86
        }
585
1.12G
        if (data_offset >= helper->dst->len) {
586
181
          g_set_error_literal(error,
587
181
                  FWUPD_ERROR,
588
181
                  FWUPD_ERROR_INVALID_DATA,
589
181
                  "bad table");
590
181
          return FALSE;
591
181
        }
592
1.12G
        helper->dst->data[dst_offset++] = helper->dst->data[data_offset++];
593
1.12G
        bytes_remaining--;
594
1.12G
      }
595
8.05M
    }
596
396M
  }
597
598
  /* success */
599
1.26k
  return TRUE;
600
4.13k
}
601
602
static gboolean
603
fu_efi_lz77_decompressor_parse(FuFirmware *firmware,
604
             GInputStream *stream,
605
             FuFirmwareParseFlags flags,
606
             GError **error)
607
2.67k
{
608
2.67k
  gsize streamsz = 0;
609
2.67k
  guint32 dst_bufsz;
610
2.67k
  guint32 src_bufsz;
611
2.67k
  g_autoptr(FuStructEfiLz77DecompressorHeader) st = NULL;
612
2.67k
  g_autoptr(GError) error_all = NULL;
613
2.67k
  g_autoptr(GByteArray) dst = g_byte_array_new();
614
2.67k
  FuEfiLz77DecompressorVersion decompressor_versions[] = {
615
2.67k
      FU_EFI_LZ77_DECOMPRESSOR_VERSION_LEGACY,
616
2.67k
      FU_EFI_LZ77_DECOMPRESSOR_VERSION_TIANO,
617
2.67k
  };
618
619
  /* parse header */
620
2.67k
  if (!fu_input_stream_size(stream, &streamsz, error))
621
0
    return FALSE;
622
2.67k
  st = fu_struct_efi_lz77_decompressor_header_parse_stream(stream, 0x0, error);
623
2.67k
  if (st == NULL)
624
16
    return FALSE;
625
2.65k
  src_bufsz = fu_struct_efi_lz77_decompressor_header_get_src_size(st);
626
2.65k
  if (streamsz < src_bufsz + st->buf->len) {
627
67
    g_set_error_literal(error,
628
67
            FWUPD_ERROR,
629
67
            FWUPD_ERROR_INVALID_DATA,
630
67
            "source buffer is truncated");
631
67
    return FALSE;
632
67
  }
633
2.58k
  dst_bufsz = fu_struct_efi_lz77_decompressor_header_get_dst_size(st);
634
2.58k
  if (dst_bufsz == 0) {
635
15
    g_set_error_literal(error,
636
15
            FWUPD_ERROR,
637
15
            FWUPD_ERROR_INVALID_DATA,
638
15
            "destination size is zero");
639
15
    return FALSE;
640
15
  }
641
2.57k
  if (dst_bufsz > fu_firmware_get_size_max(firmware)) {
642
304
    g_autofree gchar *sz_val = g_format_size(dst_bufsz);
643
304
    g_autofree gchar *sz_max = g_format_size(fu_firmware_get_size_max(firmware));
644
304
    g_set_error(error,
645
304
          FWUPD_ERROR,
646
304
          FWUPD_ERROR_INVALID_DATA,
647
304
          "destination size is too large (%s, limit %s)",
648
304
          sz_val,
649
304
          sz_max);
650
304
    return FALSE;
651
304
  }
652
2.26k
  fu_byte_array_set_size(dst, dst_bufsz, 0x0);
653
654
  /* try both position */
655
5.14k
  for (guint i = 0; i < G_N_ELEMENTS(decompressor_versions); i++) {
656
4.13k
    FuEfiLz77DecompressHelper helper = {
657
4.13k
        .dst = dst,
658
4.13k
        .stream = stream,
659
4.13k
    };
660
4.13k
    g_autoptr(GError) error_local = NULL;
661
662
4.13k
    if (!g_seekable_seek(G_SEEKABLE(stream), st->buf->len, G_SEEK_SET, NULL, error))
663
0
      return FALSE;
664
4.13k
    if (fu_efi_lz77_decompressor_internal(&helper,
665
4.13k
                  decompressor_versions[i],
666
4.13k
                  &error_local)) {
667
1.26k
      g_autoptr(GBytes) blob =
668
1.26k
          g_byte_array_free_to_bytes(g_steal_pointer(&dst)); /* nocheck:blocked */
669
1.26k
      if (!fu_firmware_set_stream(firmware, NULL, error))
670
0
        return FALSE;
671
1.26k
      fu_firmware_set_bytes(firmware, blob);
672
1.26k
      fu_firmware_set_version_raw(firmware, decompressor_versions[i]);
673
1.26k
      return TRUE;
674
1.26k
    }
675
2.87k
    if (error_all == NULL) {
676
1.86k
      g_propagate_prefixed_error(
677
1.86k
          &error_all,
678
1.86k
          g_steal_pointer(&error_local),
679
1.86k
          "failed to parse %s: ",
680
1.86k
          fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i]));
681
1.86k
      continue;
682
1.86k
    }
683
1.00k
    g_prefix_error(&error_all, /* nocheck:error */
684
1.00k
             "failed to parse %s: %s: ",
685
1.00k
             fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i]),
686
1.00k
             error_local->message);
687
1.00k
  }
688
689
  /* success */
690
1.00k
  g_propagate_error(error, g_steal_pointer(&error_all));
691
1.00k
  return FALSE;
692
2.26k
}
693
694
static void
695
fu_efi_lz77_decompressor_init(FuEfiLz77Decompressor *self)
696
2.67k
{
697
2.67k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
698
2.67k
  fu_firmware_set_size_max(FU_FIRMWARE(self), 64 * 1024 * 1024);
699
2.67k
}
700
701
static void
702
fu_efi_lz77_decompressor_class_init(FuEfiLz77DecompressorClass *klass)
703
2
{
704
2
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
705
2
  firmware_class->parse = fu_efi_lz77_decompressor_parse;
706
2
}
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
1.78k
{
718
1.78k
  return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_LZ77_DECOMPRESSOR, NULL));
719
1.78k
}