Coverage Report

Created: 2026-04-12 06:15

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
23.0k
{
93
23.0k
  size_t more = state->size ? state->size * 2 : start;
94
23.0k
  char *b = realloc (state->buffer, more);
95
23.0k
  while (unlikely (b == NULL) && more >= state->size + 1024)
96
0
    b = realloc (state->buffer, more -= 1024);
97
23.0k
  if (unlikely (b == NULL))
98
0
    return false;
99
23.0k
  state->buffer = b;
100
23.0k
  state->size = more;
101
23.0k
  return true;
102
23.0k
}
103
104
static inline void
105
smaller_buffer (struct unzip_state *state, size_t end)
106
3.74k
{
107
3.74k
  state->buffer =
108
3.74k
      realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
109
3.74k
  state->size = end;
110
3.74k
}
111
112
static inline Dwfl_Error
113
fail (struct unzip_state *state, Dwfl_Error failure)
114
943
{
115
943
  if (state->input_pos == (off_t) state->mapped_size)
116
0
    *state->whole = state->input_buffer;
117
943
  else
118
943
    {
119
943
      free (state->input_buffer);
120
943
      *state->whole = NULL;
121
943
    }
122
943
  free (state->buffer);
123
943
  return failure;
124
943
}
125
126
#ifndef ZSTD
127
static inline Dwfl_Error
128
zlib_fail (struct unzip_state *state, int result)
129
604
{
130
604
  switch (result)
131
604
    {
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
604
    default:
137
604
      return fail (state, DWFL_E_ZLIB);
138
604
    }
139
604
}
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
4.34k
{
146
4.34k
    int d = dup (fd);
147
4.34k
    if (unlikely (d < 0))
148
0
      return DWFL_E_ERRNO;
149
4.34k
    if (start_offset != 0)
150
125
      {
151
125
  off_t off = lseek (d, start_offset, SEEK_SET);
152
125
  if (off != start_offset)
153
0
    {
154
0
      close (d);
155
0
      return DWFL_E_ERRNO;
156
0
    }
157
125
      }
158
4.34k
    state->zf = gzdopen (d, "r");
159
4.34k
    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
4.34k
    return DWFL_E_NOERROR;
168
4.34k
}
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
4.68k
{
183
4.68k
  struct unzip_state state =
184
4.68k
    {
185
4.68k
#if !USE_INFLATE
186
4.68k
      .zf = NULL,
187
4.68k
#endif
188
4.68k
      .mapped_size = _mapped_size,
189
4.68k
      .whole = _whole,
190
4.68k
      .buffer = NULL,
191
4.68k
      .size = 0,
192
4.68k
      .input_buffer = NULL,
193
4.68k
      .input_pos = 0
194
4.68k
    };
195
196
4.68k
  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
4.68k
#define NOMAGIC(magic) \
221
4.68k
  (state.mapped_size <= sizeof magic || \
222
4.68k
   memcmp (mapped, magic, sizeof magic - 1))
223
224
  /* First, look at the header.  */
225
4.68k
  if (NOMAGIC (MAGIC)
226
#ifdef MAGIC2
227
      && NOMAGIC (MAGIC2)
228
#endif
229
4.68k
      )
230
    /* Not a compressed file.  */
231
339
    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
4.34k
  Dwfl_Error result = open_stream (fd, start_offset, &state);
361
362
4.34k
  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
4.34k
  if (result != DWFL_E_NOERROR)
370
0
    return fail (&state, result);
371
372
4.34k
  ptrdiff_t pos = 0;
373
23.0k
  while (1)
374
23.0k
    {
375
23.0k
      if (!bigger_buffer (&state, 1024))
376
0
  {
377
0
    gzclose (state.zf);
378
0
    return zlib_fail (&state, Z (MEM_ERROR));
379
0
  }
380
23.0k
      int n = gzread (state.zf, state.buffer + pos, state.size - pos);
381
23.0k
      if (n < 0)
382
604
  {
383
604
    int code;
384
604
    gzerror (state.zf, &code);
385
604
    gzclose (state.zf);
386
604
    return zlib_fail (&state, code);
387
604
  }
388
22.4k
      if (n == 0)
389
3.74k
  break;
390
18.7k
      pos += n;
391
18.7k
    }
392
393
3.74k
  gzclose (state.zf);
394
3.74k
  smaller_buffer (&state, pos);
395
3.74k
#endif
396
397
3.74k
  free (state.input_buffer);
398
399
3.74k
  *state.whole = state.buffer;
400
3.74k
  *whole_size = state.size;
401
402
3.74k
  return DWFL_E_NOERROR;
403
4.34k
}