Coverage Report

Created: 2023-06-07 06:53

/src/libagdb/libfwnt/libfwnt_lznt1.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * LZNT1 (de)compression functions
3
 *
4
 * Copyright (C) 2009-2022, Joachim Metz <joachim.metz@gmail.com>
5
 *
6
 * Refer to AUTHORS for acknowledgements.
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
#include <common.h>
23
#include <byte_stream.h>
24
#include <memory.h>
25
#include <types.h>
26
27
#include "libfwnt_libcerror.h"
28
#include "libfwnt_libcnotify.h"
29
#include "libfwnt_lznt1.h"
30
31
/* Decompresses a LZNT1 compressed chunk
32
 * Returns 1 on success or -1 on error
33
 */
34
int libfwnt_lznt1_decompress_chunk(
35
     const uint8_t *compressed_data,
36
     size_t compressed_data_size,
37
     size_t *compressed_data_offset,
38
     size_t compression_chunk_size,
39
     uint8_t *uncompressed_data,
40
     size_t *uncompressed_data_size,
41
     libcerror_error_t **error )
42
1.54k
{
43
1.54k
  static char *function                   = "libfwnt_lznt1_decompress_chunk";
44
1.54k
  size_t compression_tuple_index          = 0;
45
1.54k
  size_t compression_tuple_threshold      = 0;
46
1.54k
  size_t safe_compressed_data_offset      = 0;
47
1.54k
  size_t safe_uncompressed_data_size      = 0;
48
1.54k
  size_t uncompressed_data_offset         = 0;
49
1.54k
  uint16_t compression_tuple              = 0;
50
1.54k
  uint16_t compression_tuple_offset_shift = 0;
51
1.54k
  uint16_t compression_tuple_size         = 0;
52
1.54k
  uint16_t compression_tuple_size_mask    = 0;
53
1.54k
  int16_t compression_tuple_offset        = 0;
54
1.54k
  uint8_t compression_flag_bit_index      = 0;
55
1.54k
  uint8_t compression_flag_byte           = 0;
56
57
1.54k
  if( compressed_data == NULL )
58
0
  {
59
0
    libcerror_error_set(
60
0
     error,
61
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
62
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
63
0
     "%s: invalid compressed data.",
64
0
     function );
65
66
0
    return( -1 );
67
0
  }
68
1.54k
  if( compressed_data_size > (size_t) SSIZE_MAX )
69
0
  {
70
0
    libcerror_error_set(
71
0
     error,
72
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
73
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
74
0
     "%s: invalid compressed data size value exceeds maximum.",
75
0
     function );
76
77
0
    return( -1 );
78
0
  }
79
1.54k
  if( compressed_data_offset == NULL )
80
0
  {
81
0
    libcerror_error_set(
82
0
     error,
83
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
84
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
85
0
     "%s: invalid compressed data offset.",
86
0
     function );
87
88
0
    return( -1 );
89
0
  }
90
1.54k
  safe_compressed_data_offset = *compressed_data_offset;
91
92
1.54k
  if( safe_compressed_data_offset >= compressed_data_size )
93
0
  {
94
0
    libcerror_error_set(
95
0
     error,
96
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
97
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
98
0
     "%s: invalid compressed data offset value out of bounds.",
99
0
     function );
100
101
0
    return( -1 );
102
0
  }
103
1.54k
  if( compression_chunk_size > (size_t) SSIZE_MAX )
104
0
  {
105
0
    libcerror_error_set(
106
0
     error,
107
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
108
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
109
0
     "%s: invalid compression chunk size value exceeds maximum.",
110
0
     function );
111
112
0
    return( -1 );
113
0
  }
114
1.54k
  if( compression_chunk_size > ( compressed_data_size - safe_compressed_data_offset ) )
115
0
  {
116
0
    libcerror_error_set(
117
0
     error,
118
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
119
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
120
0
     "%s: compressed data too small.",
121
0
     function );
122
123
0
    return( -1 );
124
0
  }
125
1.54k
  if( uncompressed_data == NULL )
126
0
  {
127
0
    libcerror_error_set(
128
0
     error,
129
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
130
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
131
0
     "%s: invalid uncompressed data.",
132
0
     function );
133
134
0
    return( -1 );
135
0
  }
136
1.54k
  if( uncompressed_data_size == NULL )
137
0
  {
138
0
    libcerror_error_set(
139
0
     error,
140
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
141
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
142
0
     "%s: invalid uncompressed data size.",
143
0
     function );
144
145
0
    return( -1 );
146
0
  }
147
1.54k
  safe_uncompressed_data_size = *uncompressed_data_size;
148
149
1.54k
  compression_tuple_threshold    = 16;
150
1.54k
  compression_tuple_offset_shift = 12;
151
1.54k
  compression_tuple_size_mask    = 0x0fff;
152
153
7.20k
  while( compression_chunk_size > 0 )
154
5.73k
  {
155
5.73k
    if( safe_compressed_data_offset >= compressed_data_size )
156
0
    {
157
0
      break;
158
0
    }
159
5.73k
    compression_flag_byte = compressed_data[ safe_compressed_data_offset ];
160
161
#if defined( HAVE_DEBUG_OUTPUT )
162
    if( libcnotify_verbose != 0 )
163
    {
164
      libcnotify_printf(
165
       "%s: compressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n",
166
       function,
167
       safe_compressed_data_offset,
168
       safe_compressed_data_offset );
169
170
      libcnotify_printf(
171
       "%s: compression flag byte\t\t\t: 0x%02" PRIx8 "\n",
172
       function,
173
       compression_flag_byte );
174
175
      libcnotify_printf(
176
       "\n" );
177
    }
178
#endif /* #if defined( HAVE_DEBUG_OUTPUT ) */
179
180
5.73k
    safe_compressed_data_offset += 1;
181
5.73k
    compression_chunk_size      -= 1;
182
183
5.73k
    for( compression_flag_bit_index = 0;
184
44.4k
         compression_flag_bit_index < 8;
185
38.7k
         compression_flag_bit_index++ )
186
40.2k
    {
187
      /* Check if the data is compressed (tag bit is set)
188
       */
189
40.2k
      if( ( compression_flag_byte & 0x01 ) != 0 )
190
5.05k
      {
191
5.05k
        if( safe_compressed_data_offset >= ( compressed_data_size - 1 ) )
192
23
        {
193
23
          libcerror_error_set(
194
23
           error,
195
23
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
196
23
           LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
197
23
           "%s: invalid compressed data offset value out of bounds.",
198
23
           function );
199
200
23
          return( -1 );
201
23
        }
202
        /* Read the compression ( size, offset ) tuple
203
         */
204
5.03k
        byte_stream_copy_to_uint16_little_endian(
205
5.03k
         &( compressed_data[ safe_compressed_data_offset ] ),
206
5.03k
         compression_tuple );
207
208
5.03k
        compression_tuple_offset = ( compression_tuple >> compression_tuple_offset_shift ) + 1;
209
5.03k
        compression_tuple_size   = ( compression_tuple & compression_tuple_size_mask ) + 3;
210
211
#if defined( HAVE_DEBUG_OUTPUT )
212
        if( libcnotify_verbose != 0 )
213
        {
214
          libcnotify_printf(
215
           "%s: compressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n",
216
           function,
217
           safe_compressed_data_offset,
218
           safe_compressed_data_offset );
219
220
          libcnotify_printf(
221
           "%s: compression tuple\t\t\t: 0x%04" PRIx16 " (shift: %" PRIu16 ", mask: 0x%04" PRIx16 ")\n",
222
           function,
223
           compression_tuple,
224
           compression_tuple_offset_shift,
225
           compression_tuple_size_mask );
226
227
          libcnotify_printf(
228
           "%s: compression tuple offset\t\t: %" PRIi16 "\n",
229
           function,
230
           compression_tuple_offset );
231
232
          libcnotify_printf(
233
           "%s: compression tuple size\t\t\t: %" PRIu16 "\n",
234
           function,
235
           compression_tuple_size );
236
237
          libcnotify_printf(
238
           "%s: uncompressed data offset\t\t: %" PRIzd "\n",
239
           function,
240
           uncompressed_data_offset );
241
242
          libcnotify_printf(
243
           "\n" );
244
        }
245
#endif /* #if defined( HAVE_DEBUG_OUTPUT ) */
246
247
5.03k
        safe_compressed_data_offset += 2;
248
5.03k
        compression_chunk_size      -= 2;
249
250
        /* The compression tuple offset refers to an offset in the uncompressed data
251
         */
252
5.03k
        if( (size_t) compression_tuple_offset > uncompressed_data_offset )
253
36
        {
254
36
          libcerror_error_set(
255
36
           error,
256
36
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
257
36
           LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
258
36
           "%s: invalid compression tuple offset value out of bounds.",
259
36
           function );
260
261
36
          return( -1 );
262
36
        }
263
4.99k
        compression_tuple_index = uncompressed_data_offset - compression_tuple_offset;
264
265
2.82M
        while( compression_tuple_size > 0 )
266
2.82M
        {
267
2.82M
          if( compression_tuple_index > uncompressed_data_offset )
268
0
          {
269
0
            libcerror_error_set(
270
0
             error,
271
0
             LIBCERROR_ERROR_DOMAIN_RUNTIME,
272
0
             LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
273
0
             "%s: invalid compression tuple index value out of bounds.",
274
0
             function );
275
276
0
            return( -1 );
277
0
          }
278
2.82M
          if( uncompressed_data_offset >= safe_uncompressed_data_size )
279
3
          {
280
3
            libcerror_error_set(
281
3
             error,
282
3
             LIBCERROR_ERROR_DOMAIN_RUNTIME,
283
3
             LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
284
3
             "%s: invalid uncompressed data offset value out of bounds.",
285
3
             function );
286
287
3
            return( -1 );
288
3
          }
289
2.82M
          uncompressed_data[ uncompressed_data_offset++ ] = uncompressed_data[ compression_tuple_index++ ];
290
291
2.82M
          compression_tuple_size -= 1;
292
2.82M
        }
293
4.99k
      }
294
35.2k
      else
295
35.2k
      {
296
35.2k
        if( uncompressed_data_offset >= safe_uncompressed_data_size )
297
3
        {
298
3
          libcerror_error_set(
299
3
           error,
300
3
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
301
3
           LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
302
3
           "%s: invalid uncompressed data offset value out of bounds.",
303
3
           function );
304
305
3
          return( -1 );
306
3
        }
307
35.2k
        if( safe_compressed_data_offset >= compressed_data_size )
308
11
        {
309
11
          libcerror_error_set(
310
11
           error,
311
11
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
312
11
           LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
313
11
           "%s: invalid compressed data offset value out of bounds.",
314
11
           function );
315
316
11
          return( -1 );
317
11
        }
318
35.1k
        uncompressed_data[ uncompressed_data_offset++ ] = compressed_data[ safe_compressed_data_offset++ ];
319
320
35.1k
        compression_chunk_size -= 1;
321
35.1k
      }
322
40.1k
      compression_flag_byte >>= 1;
323
324
40.1k
      if( compression_chunk_size == 0 )
325
1.46k
      {
326
1.46k
        break;
327
1.46k
      }
328
      /* The compression tuple size mask and offset shift
329
       * are dependent on the current buffer offset in the uncompressed data
330
       */
331
42.8k
      while( uncompressed_data_offset > compression_tuple_threshold )
332
4.15k
      {
333
4.15k
        if( compression_tuple_offset_shift == 0 )
334
0
        {
335
0
          libcerror_error_set(
336
0
           error,
337
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
338
0
           LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
339
0
           "%s: invalid compression tuple offset shift value out of bounds.",
340
0
           function );
341
342
0
          return( -1 );
343
0
        }
344
4.15k
        compression_tuple_offset_shift -= 1;
345
4.15k
        compression_tuple_size_mask   >>= 1;
346
4.15k
        compression_tuple_threshold   <<= 1;
347
4.15k
      }
348
38.7k
    }
349
5.73k
  }
350
1.46k
  *compressed_data_offset = safe_compressed_data_offset;
351
1.46k
  *uncompressed_data_size = uncompressed_data_offset;
352
353
1.46k
  return( 1 );
354
1.54k
}
355
356
/* Decompresses data using LZNT1 compression
357
 * Returns 1 on success or -1 on error
358
 */
359
int libfwnt_lznt1_decompress(
360
     const uint8_t *compressed_data,
361
     size_t compressed_data_size,
362
     uint8_t *uncompressed_data,
363
     size_t *uncompressed_data_size,
364
     libcerror_error_t **error )
365
1.66k
{
366
1.66k
  static char *function              = "libfwnt_lznt1_decompress";
367
1.66k
  size_t compressed_data_offset      = 0;
368
1.66k
  size_t safe_uncompressed_data_size = 0;
369
1.66k
  size_t uncompressed_chunk_size     = 0;
370
1.66k
  size_t uncompressed_data_offset    = 0;
371
1.66k
  uint16_t compression_chunk_header  = 0;
372
1.66k
  uint16_t compression_chunk_size    = 0;
373
374
1.66k
  if( compressed_data == NULL )
375
0
  {
376
0
    libcerror_error_set(
377
0
     error,
378
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
379
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
380
0
     "%s: invalid compressed data.",
381
0
     function );
382
383
0
    return( -1 );
384
0
  }
385
1.66k
  if( compressed_data_size > (size_t) SSIZE_MAX )
386
0
  {
387
0
    libcerror_error_set(
388
0
     error,
389
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
390
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
391
0
     "%s: invalid compressed data size value exceeds maximum.",
392
0
     function );
393
394
0
    return( -1 );
395
0
  }
396
1.66k
  if( uncompressed_data == NULL )
397
0
  {
398
0
    libcerror_error_set(
399
0
     error,
400
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
401
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
402
0
     "%s: invalid uncompressed data.",
403
0
     function );
404
405
0
    return( -1 );
406
0
  }
407
1.66k
  if( uncompressed_data_size == NULL )
408
0
  {
409
0
    libcerror_error_set(
410
0
     error,
411
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
412
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
413
0
     "%s: invalid uncompressed data size.",
414
0
     function );
415
416
0
    return( -1 );
417
0
  }
418
1.66k
  safe_uncompressed_data_size = *uncompressed_data_size;
419
420
3.25k
  while( compressed_data_offset < compressed_data_size )
421
1.66k
  {
422
1.66k
    if( uncompressed_data_offset >= safe_uncompressed_data_size )
423
0
    {
424
0
      break;
425
0
    }
426
1.66k
    if( ( compressed_data_offset + 1 ) >= compressed_data_size )
427
0
    {
428
0
      libcerror_error_set(
429
0
       error,
430
0
       LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
431
0
       LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
432
0
       "%s: compressed data size value too small.",
433
0
       function );
434
435
0
      return( -1 );
436
0
    }
437
    /* The first 2 bytes contain the compressed chunk header
438
     * 0  - 11  compressed chunk size
439
     * 12 - 14  signature value
440
     * 15   is compressed flag
441
     */
442
1.66k
    byte_stream_copy_to_uint16_little_endian(
443
1.66k
     &( compressed_data[ compressed_data_offset ] ),
444
1.66k
     compression_chunk_header );
445
446
#if defined( HAVE_DEBUG_OUTPUT )
447
    if( libcnotify_verbose != 0 )
448
    {
449
      libcnotify_printf(
450
       "%s: compressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n",
451
       function,
452
       compressed_data_offset,
453
       compressed_data_offset );
454
455
      libcnotify_printf(
456
       "%s: compression chunk header\t\t\t: 0x%04" PRIx16 "\n",
457
       function,
458
       compression_chunk_header );
459
460
      libcnotify_printf(
461
       "%s: compressed chunk size\t\t\t\t: %" PRIu16 "\n",
462
       function,
463
       ( compression_chunk_header & 0x0fff ) + 1 );
464
465
      libcnotify_printf(
466
       "%s: signature value\t\t\t\t: %" PRIu16 "\n",
467
       function,
468
       ( compression_chunk_header >> 12 ) & 0x0007 );
469
470
      libcnotify_printf(
471
       "%s: is compressed flag\t\t\t\t: %" PRIu16 "\n",
472
       function,
473
       compression_chunk_header >> 15 );
474
475
      libcnotify_printf(
476
       "\n" );
477
    }
478
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
479
480
1.66k
    compressed_data_offset += 2;
481
482
1.66k
    if( compression_chunk_header == 0 )
483
0
    {
484
0
      break;
485
0
    }
486
1.66k
    compression_chunk_size = ( compression_chunk_header & 0x0fff ) + 1;
487
488
1.66k
    if( ( compression_chunk_header & 0x8000 ) != 0 )
489
1.54k
    {
490
      /* Adjust the compression chunk size for the iteration
491
       */
492
1.54k
      uncompressed_chunk_size = safe_uncompressed_data_size - uncompressed_data_offset;
493
494
1.54k
      if( libfwnt_lznt1_decompress_chunk(
495
1.54k
           compressed_data,
496
1.54k
           compressed_data_size,
497
1.54k
           &compressed_data_offset,
498
1.54k
           compression_chunk_size,
499
1.54k
           &( uncompressed_data[ uncompressed_data_offset ] ),
500
1.54k
           &uncompressed_chunk_size,
501
1.54k
           error ) != 1 )
502
76
      {
503
76
        libcerror_error_set(
504
76
         error,
505
76
         LIBCERROR_ERROR_DOMAIN_COMPRESSION,
506
76
         LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
507
76
         "%s: unable to decompress chunk.",
508
76
         function );
509
510
76
        return( -1 );
511
76
      }
512
1.46k
      uncompressed_data_offset += uncompressed_chunk_size;
513
1.46k
    }
514
120
    else
515
120
    {
516
120
      if( compression_chunk_size > ( compressed_data_size - compressed_data_offset ) )
517
0
      {
518
0
        libcerror_error_set(
519
0
         error,
520
0
         LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
521
0
         LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
522
0
         "%s: compressed data too small.",
523
0
         function );
524
525
0
        return( -1 );
526
0
      }
527
120
      if( compression_chunk_size > ( safe_uncompressed_data_size - uncompressed_data_offset ) )
528
2
      {
529
2
        libcerror_error_set(
530
2
         error,
531
2
         LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
532
2
         LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
533
2
         "%s: uncompressed data too small.",
534
2
         function );
535
536
2
        return( -1 );
537
2
      }
538
118
      if( memory_copy(
539
118
           &( uncompressed_data[ uncompressed_data_offset ] ),
540
118
           &( compressed_data[ compressed_data_offset ] ),
541
118
           compression_chunk_size ) == NULL )
542
0
      {
543
0
        libcerror_error_set(
544
0
         error,
545
0
         LIBCERROR_ERROR_DOMAIN_MEMORY,
546
0
         LIBCERROR_MEMORY_ERROR_COPY_FAILED,
547
0
         "%s: unable to copy copy compressed data to uncompressed data.",
548
0
         function );
549
550
0
        return( -1 );
551
0
      }
552
118
      compressed_data_offset   += (size_t) compression_chunk_size;
553
118
      uncompressed_data_offset += (size_t) compression_chunk_size;
554
118
    }
555
1.66k
  }
556
1.58k
  *uncompressed_data_size = uncompressed_data_offset;
557
558
1.58k
  return( 1 );
559
1.66k
}
560