Coverage Report

Created: 2025-12-27 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/elfutils/libdwfl/gzip.c
Line
Count
Source
1
/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
2
   Copyright (C) 2009 Red Hat, Inc.
3
   This file is part of elfutils.
4
5
   This file is free software; you can redistribute it and/or modify
6
   it under the terms of either
7
8
     * the GNU Lesser General Public License as published by the Free
9
       Software Foundation; either version 3 of the License, or (at
10
       your option) any later version
11
12
   or
13
14
     * the GNU General Public License as published by the Free
15
       Software Foundation; either version 2 of the License, or (at
16
       your option) any later version
17
18
   or both in parallel, as here.
19
20
   elfutils is distributed in the hope that it will be useful, but
21
   WITHOUT ANY WARRANTY; without even the implied warranty of
22
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
   General Public License for more details.
24
25
   You should have received copies of the GNU General Public License and
26
   the GNU Lesser General Public License along with this program.  If
27
   not, see <http://www.gnu.org/licenses/>.  */
28
29
#ifdef HAVE_CONFIG_H
30
# include <config.h>
31
#endif
32
33
#include "libdwflP.h"
34
#include "system.h"
35
36
#ifdef LZMA
37
# define USE_INFLATE  1
38
# include <lzma.h>
39
# define unzip    __libdw_unlzma
40
# define DWFL_E_ZLIB  DWFL_E_LZMA
41
# define MAGIC    "\xFD" "7zXZ\0" /* XZ file format.  */
42
# define MAGIC2   "\x5d\0"  /* Raw LZMA format.  */
43
# define Z(what)  LZMA_##what
44
# define LZMA_ERRNO LZMA_PROG_ERROR
45
# define z_stream lzma_stream
46
# define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0)
47
# define do_inflate(z)  lzma_code (z, LZMA_RUN)
48
# define inflateEnd(z)  lzma_end (z)
49
#elif defined ZSTD
50
# define USE_INFLATE  1
51
# include <zstd.h>
52
# define unzip    __libdw_unzstd
53
# define DWFL_E_ZLIB  DWFL_E_ZSTD
54
# define MAGIC    "\x28\xb5\x2f\xfd"
55
#elif defined BZLIB
56
# define USE_INFLATE  1
57
# include <bzlib.h>
58
# define unzip    __libdw_bunzip2
59
# define DWFL_E_ZLIB  DWFL_E_BZLIB
60
# define MAGIC    "BZh"
61
# define Z(what)  BZ_##what
62
# define BZ_ERRNO BZ_IO_ERROR
63
# define z_stream bz_stream
64
# define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
65
# define do_inflate(z)  BZ2_bzDecompress (z)
66
# define inflateEnd(z)  BZ2_bzDecompressEnd (z)
67
#else
68
# define USE_INFLATE  0
69
# define crc32    loser_crc32
70
# include <zlib.h>
71
# define unzip    __libdw_gunzip
72
# define MAGIC    "\037\213"
73
0
# define Z(what)  Z_##what
74
#endif
75
76
0
#define READ_SIZE   (1 << 20)
77
78
struct unzip_state {
79
#if !USE_INFLATE
80
  gzFile zf;
81
#endif
82
  size_t mapped_size;
83
  void **whole;
84
  void *buffer;
85
  size_t size;
86
  void *input_buffer;
87
  off_t input_pos;
88
};
89
90
static inline bool
91
bigger_buffer (struct unzip_state *state, size_t start)
92
741
{
93
741
  size_t more = state->size ? state->size * 2 : start;
94
741
  char *b = realloc (state->buffer, more);
95
741
  while (unlikely (b == NULL) && more >= state->size + 1024)
96
0
    b = realloc (state->buffer, more -= 1024);
97
741
  if (unlikely (b == NULL))
98
0
    return false;
99
741
  state->buffer = b;
100
741
  state->size = more;
101
741
  return true;
102
741
}
103
104
static inline void
105
smaller_buffer (struct unzip_state *state, size_t end)
106
51
{
107
51
  state->buffer =
108
51
      realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
109
51
  state->size = end;
110
51
}
111
112
static inline Dwfl_Error
113
fail (struct unzip_state *state, Dwfl_Error failure)
114
10
{
115
10
  if (state->input_pos == (off_t) state->mapped_size)
116
0
    *state->whole = state->input_buffer;
117
10
  else
118
10
    {
119
10
      free (state->input_buffer);
120
10
      *state->whole = NULL;
121
10
    }
122
10
  free (state->buffer);
123
10
  return failure;
124
10
}
125
126
#ifndef ZSTD
127
static inline Dwfl_Error
128
zlib_fail (struct unzip_state *state, int result)
129
5
{
130
5
  switch (result)
131
5
    {
132
0
    case Z (MEM_ERROR):
133
0
      return fail (state, DWFL_E_NOMEM);
134
0
    case Z (ERRNO):
135
0
      return fail (state, DWFL_E_ERRNO);
136
5
    default:
137
5
      return fail (state, DWFL_E_ZLIB);
138
5
    }
139
5
}
140
#endif
141
142
#if !USE_INFLATE
143
static Dwfl_Error
144
open_stream (int fd, off_t start_offset, struct unzip_state *state)
145
56
{
146
56
    int d = dup (fd);
147
56
    if (unlikely (d < 0))
148
0
      return DWFL_E_ERRNO;
149
56
    if (start_offset != 0)
150
9
      {
151
9
  off_t off = lseek (d, start_offset, SEEK_SET);
152
9
  if (off != start_offset)
153
0
    {
154
0
      close (d);
155
0
      return DWFL_E_ERRNO;
156
0
    }
157
9
      }
158
56
    state->zf = gzdopen (d, "r");
159
56
    if (unlikely (state->zf == NULL))
160
0
      {
161
0
  close (d);
162
0
  return DWFL_E_NOMEM;
163
0
      }
164
165
    /* From here on, zlib will close D.  */
166
167
56
    return DWFL_E_NOERROR;
168
56
}
169
#endif
170
171
/* If this is not a compressed image, return DWFL_E_BADELF.
172
   If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
173
   Otherwise return an error for bad compressed data or I/O failure.
174
   If we return an error after reading the first part of the file,
175
   leave that portion malloc'd in *WHOLE, *WHOLE_SIZE.  If *WHOLE
176
   is not null on entry, we'll use it in lieu of repeating a read.  */
177
178
Dwfl_Error internal_function
179
unzip (int fd, off_t start_offset,
180
       void *mapped, size_t _mapped_size,
181
       void **_whole, size_t *whole_size)
182
61
{
183
61
  struct unzip_state state =
184
61
    {
185
61
#if !USE_INFLATE
186
61
      .zf = NULL,
187
61
#endif
188
61
      .mapped_size = _mapped_size,
189
61
      .whole = _whole,
190
61
      .buffer = NULL,
191
61
      .size = 0,
192
61
      .input_buffer = NULL,
193
61
      .input_pos = 0
194
61
    };
195
196
61
  if (mapped == NULL)
197
0
    {
198
0
      if (*state.whole == NULL)
199
0
  {
200
0
    state.input_buffer = malloc (READ_SIZE);
201
0
    if (unlikely (state.input_buffer == NULL))
202
0
      return DWFL_E_NOMEM;
203
204
0
    ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
205
0
    if (unlikely (n < 0))
206
0
      return fail (&state, DWFL_E_ERRNO);
207
208
0
    state.input_pos = n;
209
0
    mapped = state.input_buffer;
210
0
    state.mapped_size = n;
211
0
  }
212
0
      else
213
0
  {
214
0
    state.input_buffer = *state.whole;
215
0
    mapped = state.input_buffer;
216
0
    state.input_pos = state.mapped_size = *whole_size;
217
0
  }
218
0
    }
219
220
61
#define NOMAGIC(magic) \
221
61
  (state.mapped_size <= sizeof magic || \
222
61
   memcmp (mapped, magic, sizeof magic - 1))
223
224
  /* First, look at the header.  */
225
61
  if (NOMAGIC (MAGIC)
226
#ifdef MAGIC2
227
      && NOMAGIC (MAGIC2)
228
#endif
229
61
      )
230
    /* Not a compressed file.  */
231
5
    return fail (&state, DWFL_E_BADELF);
232
233
#ifdef ZSTD
234
  /* special case for libzstd since it is slightly different from the
235
     API provided by bzlib and liblzma.  */
236
237
  void *next_in = mapped;
238
  size_t avail_in = state.mapped_size;
239
  void *next_out = NULL;
240
  size_t avail_out = 0;
241
  size_t total_out = 0;
242
243
  size_t result;
244
  ZSTD_DCtx *dctx = ZSTD_createDCtx();
245
  if (dctx == NULL)
246
    return fail (&state, DWFL_E_NOMEM);
247
248
  do
249
    {
250
      if (avail_in == 0 && state.input_buffer != NULL)
251
  {
252
    ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
253
           start_offset + state.input_pos);
254
    if (unlikely (n < 0))
255
      {
256
        ZSTD_freeDCtx (dctx);
257
        return fail (&state, DWFL_E_ERRNO);
258
      }
259
    next_in = state.input_buffer;
260
    avail_in = n;
261
    state.input_pos += n;
262
  }
263
      if (avail_out == 0)
264
  {
265
    ptrdiff_t pos = (void *) next_out - state.buffer;
266
    if (!bigger_buffer (&state, avail_in))
267
      {
268
        ZSTD_freeDCtx (dctx);
269
        return fail (&state, DWFL_E_NOMEM);
270
      }
271
    next_out = state.buffer + pos;
272
    avail_out = state.size - pos;
273
  }
274
275
      ZSTD_inBuffer input = { next_in, avail_in, 0 };
276
      ZSTD_outBuffer output = { next_out, avail_out, 0 };
277
      result = ZSTD_decompressStream (dctx, &output, &input);
278
279
      if (! ZSTD_isError (result))
280
  {
281
    total_out += output.pos;
282
    next_out += output.pos;
283
    avail_out -= output.pos;
284
    next_in += input.pos;
285
    avail_in -= input.pos;
286
  }
287
288
      if (result == 0)
289
  break;
290
    }
291
  while (avail_in > 0 && ! ZSTD_isError (result));
292
293
  ZSTD_freeDCtx (dctx);
294
295
  if (ZSTD_isError (result))
296
    return fail (&state, DWFL_E_ZSTD);
297
298
  smaller_buffer (&state, total_out);
299
300
#elif USE_INFLATE
301
302
  /* This style actually only works with bzlib and liblzma.
303
     The stupid zlib interface has nothing to grok the
304
     gzip file headers except the slow gzFile interface.  */
305
306
  z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
307
  int result = inflateInit (&z);
308
  if (result != Z (OK))
309
    {
310
      inflateEnd (&z);
311
      return zlib_fail (&state, result);
312
    }
313
314
  do
315
    {
316
      if (z.avail_in == 0 && state.input_buffer != NULL)
317
  {
318
    ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
319
           start_offset + state.input_pos);
320
    if (unlikely (n < 0))
321
      {
322
        inflateEnd (&z);
323
        return zlib_fail (&state, Z (ERRNO));
324
      }
325
    z.next_in = state.input_buffer;
326
    z.avail_in = n;
327
    state.input_pos += n;
328
  }
329
      if (z.avail_out == 0)
330
  {
331
    ptrdiff_t pos = (void *) z.next_out - state.buffer;
332
    if (!bigger_buffer (&state, z.avail_in))
333
      {
334
        result = Z (MEM_ERROR);
335
        break;
336
      }
337
    z.next_out = state.buffer + pos;
338
    z.avail_out = state.size - pos;
339
  }
340
    }
341
  while ((result = do_inflate (&z)) == Z (OK));
342
343
#ifdef BZLIB
344
  uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
345
      | z.total_out_lo32);
346
  smaller_buffer (&state, total_out);
347
#else
348
  smaller_buffer (&state, z.total_out);
349
#endif
350
351
  inflateEnd (&z);
352
353
  if (result != Z (STREAM_END))
354
    return zlib_fail (&state, result);
355
356
#else  /* gzip only.  */
357
358
  /* Let the decompression library read the file directly.  */
359
360
56
  Dwfl_Error result = open_stream (fd, start_offset, &state);
361
362
56
  if (result == DWFL_E_NOERROR && gzdirect (state.zf))
363
0
    {
364
0
      gzclose (state.zf);
365
      /* Not a compressed stream after all.  */
366
0
      return fail (&state, DWFL_E_BADELF);
367
0
    }
368
369
56
  if (result != DWFL_E_NOERROR)
370
0
    return fail (&state, result);
371
372
56
  ptrdiff_t pos = 0;
373
741
  while (1)
374
741
    {
375
741
      if (!bigger_buffer (&state, 1024))
376
0
  {
377
0
    gzclose (state.zf);
378
0
    return zlib_fail (&state, Z (MEM_ERROR));
379
0
  }
380
741
      int n = gzread (state.zf, state.buffer + pos, state.size - pos);
381
741
      if (n < 0)
382
5
  {
383
5
    int code;
384
5
    gzerror (state.zf, &code);
385
5
    gzclose (state.zf);
386
5
    return zlib_fail (&state, code);
387
5
  }
388
736
      if (n == 0)
389
51
  break;
390
685
      pos += n;
391
685
    }
392
393
51
  gzclose (state.zf);
394
51
  smaller_buffer (&state, pos);
395
51
#endif
396
397
51
  free (state.input_buffer);
398
399
51
  *state.whole = state.buffer;
400
51
  *whole_size = state.size;
401
402
51
  return DWFL_E_NOERROR;
403
56
}