Coverage Report

Created: 2026-09-03 06:50

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
0
{
93
0
  size_t more = state->size ? state->size * 2 : start;
94
0
  char *b = realloc (state->buffer, more);
95
0
  while (unlikely (b == NULL) && more >= state->size + 1024)
96
0
    b = realloc (state->buffer, more -= 1024);
97
0
  if (unlikely (b == NULL))
98
0
    return false;
99
0
  state->buffer = b;
100
0
  state->size = more;
101
0
  return true;
102
0
}
103
104
static inline void
105
smaller_buffer (struct unzip_state *state, size_t end)
106
0
{
107
0
  state->buffer =
108
0
      realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
109
0
  state->size = end;
110
0
}
111
112
static inline Dwfl_Error
113
fail (struct unzip_state *state, Dwfl_Error failure)
114
347
{
115
347
  if (state->input_pos == (off_t) state->mapped_size)
116
0
    *state->whole = state->input_buffer;
117
347
  else
118
347
    {
119
347
      free (state->input_buffer);
120
347
      *state->whole = NULL;
121
347
    }
122
347
  free (state->buffer);
123
347
  return failure;
124
347
}
125
126
#ifndef ZSTD
127
static inline Dwfl_Error
128
zlib_fail (struct unzip_state *state, int result)
129
0
{
130
0
  switch (result)
131
0
    {
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
0
    default:
137
0
      return fail (state, DWFL_E_ZLIB);
138
0
    }
139
0
}
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
0
{
146
0
    int d = dup (fd);
147
0
    if (unlikely (d < 0))
148
0
      return DWFL_E_ERRNO;
149
0
    if (start_offset != 0)
150
0
      {
151
0
  off_t off = lseek (d, start_offset, SEEK_SET);
152
0
  if (off != start_offset)
153
0
    {
154
0
      close (d);
155
0
      return DWFL_E_ERRNO;
156
0
    }
157
0
      }
158
0
    state->zf = gzdopen (d, "r");
159
0
    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
0
    return DWFL_E_NOERROR;
168
0
}
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
347
{
183
347
  struct unzip_state state =
184
347
    {
185
347
#if !USE_INFLATE
186
347
      .zf = NULL,
187
347
#endif
188
347
      .mapped_size = _mapped_size,
189
347
      .whole = _whole,
190
347
      .buffer = NULL,
191
347
      .size = 0,
192
347
      .input_buffer = NULL,
193
347
      .input_pos = 0
194
347
    };
195
196
347
  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
    state.input_pos = state.mapped_size = *whole_size;
216
0
  }
217
0
    }
218
219
347
#define NOMAGIC(magic) \
220
347
  (state.mapped_size <= sizeof magic || \
221
347
   memcmp (mapped, magic, sizeof magic - 1))
222
223
  /* First, look at the header.  */
224
347
  if (NOMAGIC (MAGIC)
225
#ifdef MAGIC2
226
      && NOMAGIC (MAGIC2)
227
#endif
228
347
      )
229
    /* Not a compressed file.  */
230
347
    return fail (&state, DWFL_E_BADELF);
231
232
#ifdef ZSTD
233
  /* special case for libzstd since it is slightly different from the
234
     API provided by bzlib and liblzma.  */
235
236
  void *next_in = mapped;
237
  size_t avail_in = state.mapped_size;
238
  void *next_out = NULL;
239
  size_t avail_out = 0;
240
  size_t total_out = 0;
241
242
  size_t result;
243
  ZSTD_DCtx *dctx = ZSTD_createDCtx();
244
  if (dctx == NULL)
245
    return fail (&state, DWFL_E_NOMEM);
246
247
  do
248
    {
249
      if (avail_in == 0 && state.input_buffer != NULL)
250
  {
251
    ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
252
           start_offset + state.input_pos);
253
    if (unlikely (n < 0))
254
      {
255
        ZSTD_freeDCtx (dctx);
256
        return fail (&state, DWFL_E_ERRNO);
257
      }
258
    next_in = state.input_buffer;
259
    avail_in = n;
260
    state.input_pos += n;
261
  }
262
      if (avail_out == 0)
263
  {
264
    ptrdiff_t pos = (void *) next_out - state.buffer;
265
    if (!bigger_buffer (&state, avail_in))
266
      {
267
        ZSTD_freeDCtx (dctx);
268
        return fail (&state, DWFL_E_NOMEM);
269
      }
270
    next_out = state.buffer + pos;
271
    avail_out = state.size - pos;
272
  }
273
274
      ZSTD_inBuffer input = { next_in, avail_in, 0 };
275
      ZSTD_outBuffer output = { next_out, avail_out, 0 };
276
      result = ZSTD_decompressStream (dctx, &output, &input);
277
278
      if (! ZSTD_isError (result))
279
  {
280
    total_out += output.pos;
281
    next_out += output.pos;
282
    avail_out -= output.pos;
283
    next_in += input.pos;
284
    avail_in -= input.pos;
285
  }
286
287
      if (result == 0)
288
  break;
289
    }
290
  while (avail_in > 0 && ! ZSTD_isError (result));
291
292
  ZSTD_freeDCtx (dctx);
293
294
  if (ZSTD_isError (result))
295
    return fail (&state, DWFL_E_ZSTD);
296
297
  smaller_buffer (&state, total_out);
298
299
#elif USE_INFLATE
300
301
  /* This style actually only works with bzlib and liblzma.
302
     The stupid zlib interface has nothing to grok the
303
     gzip file headers except the slow gzFile interface.  */
304
305
  z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
306
  int result = inflateInit (&z);
307
  if (result != Z (OK))
308
    {
309
      inflateEnd (&z);
310
      return zlib_fail (&state, result);
311
    }
312
313
  do
314
    {
315
      if (z.avail_in == 0 && state.input_buffer != NULL)
316
  {
317
    ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
318
           start_offset + state.input_pos);
319
    if (unlikely (n < 0))
320
      {
321
        inflateEnd (&z);
322
        return zlib_fail (&state, Z (ERRNO));
323
      }
324
    z.next_in = state.input_buffer;
325
    z.avail_in = n;
326
    state.input_pos += n;
327
  }
328
      if (z.avail_out == 0)
329
  {
330
    ptrdiff_t pos = (void *) z.next_out - state.buffer;
331
    if (!bigger_buffer (&state, z.avail_in))
332
      {
333
        result = Z (MEM_ERROR);
334
        break;
335
      }
336
    z.next_out = state.buffer + pos;
337
    z.avail_out = state.size - pos;
338
  }
339
    }
340
  while ((result = do_inflate (&z)) == Z (OK));
341
342
#ifdef BZLIB
343
  uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
344
      | z.total_out_lo32);
345
  smaller_buffer (&state, total_out);
346
#else
347
  smaller_buffer (&state, z.total_out);
348
#endif
349
350
  inflateEnd (&z);
351
352
  if (result != Z (STREAM_END))
353
    return zlib_fail (&state, result);
354
355
#else  /* gzip only.  */
356
357
  /* Let the decompression library read the file directly.  */
358
359
0
  Dwfl_Error result = open_stream (fd, start_offset, &state);
360
361
0
  if (result == DWFL_E_NOERROR && gzdirect (state.zf))
362
0
    {
363
0
      gzclose (state.zf);
364
      /* Not a compressed stream after all.  */
365
0
      return fail (&state, DWFL_E_BADELF);
366
0
    }
367
368
0
  if (result != DWFL_E_NOERROR)
369
0
    return fail (&state, result);
370
371
0
  ptrdiff_t pos = 0;
372
0
  while (1)
373
0
    {
374
0
      if (!bigger_buffer (&state, 1024))
375
0
  {
376
0
    gzclose (state.zf);
377
0
    return zlib_fail (&state, Z (MEM_ERROR));
378
0
  }
379
0
      int n = gzread (state.zf, state.buffer + pos, state.size - pos);
380
0
      if (n < 0)
381
0
  {
382
0
    int code;
383
0
    gzerror (state.zf, &code);
384
0
    gzclose (state.zf);
385
0
    return zlib_fail (&state, code);
386
0
  }
387
0
      if (n == 0)
388
0
  break;
389
0
      pos += n;
390
0
    }
391
392
0
  gzclose (state.zf);
393
0
  smaller_buffer (&state, pos);
394
0
#endif
395
396
0
  free (state.input_buffer);
397
398
0
  *state.whole = state.buffer;
399
0
  *whole_size = state.size;
400
401
0
  return DWFL_E_NOERROR;
402
0
}