Coverage Report

Created: 2025-07-11 06:43

/src/elfutils/libdwfl/gzip.c
Line
Count
Source (jump to first uncovered line)
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
227
{
115
227
  if (state->input_pos == (off_t) state->mapped_size)
116
0
    *state->whole = state->input_buffer;
117
227
  else
118
227
    {
119
227
      free (state->input_buffer);
120
227
      *state->whole = NULL;
121
227
    }
122
227
  free (state->buffer);
123
227
  return failure;
124
227
}
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
227
{
183
227
  struct unzip_state state =
184
227
    {
185
227
#if !USE_INFLATE
186
227
      .zf = NULL,
187
227
#endif
188
227
      .mapped_size = _mapped_size,
189
227
      .whole = _whole,
190
227
      .buffer = NULL,
191
227
      .size = 0,
192
227
      .input_buffer = NULL,
193
227
      .input_pos = 0
194
227
    };
195
196
227
  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
227
#define NOMAGIC(magic) \
221
227
  (state.mapped_size <= sizeof magic || \
222
227
   memcmp (mapped, magic, sizeof magic - 1))
223
224
  /* First, look at the header.  */
225
227
  if (NOMAGIC (MAGIC)
226
#ifdef MAGIC2
227
      && NOMAGIC (MAGIC2)
228
#endif
229
227
      )
230
    /* Not a compressed file.  */
231
227
    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
0
  Dwfl_Error result = open_stream (fd, start_offset, &state);
361
362
0
  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
0
  if (result != DWFL_E_NOERROR)
370
0
    return fail (&state, result);
371
372
0
  ptrdiff_t pos = 0;
373
0
  while (1)
374
0
    {
375
0
      if (!bigger_buffer (&state, 1024))
376
0
  {
377
0
    gzclose (state.zf);
378
0
    return zlib_fail (&state, Z (MEM_ERROR));
379
0
  }
380
0
      int n = gzread (state.zf, state.buffer + pos, state.size - pos);
381
0
      if (n < 0)
382
0
  {
383
0
    int code;
384
0
    gzerror (state.zf, &code);
385
0
    gzclose (state.zf);
386
0
    return zlib_fail (&state, code);
387
0
  }
388
0
      if (n == 0)
389
0
  break;
390
0
      pos += n;
391
0
    }
392
393
0
  gzclose (state.zf);
394
0
  smaller_buffer (&state, pos);
395
0
#endif
396
397
0
  free (state.input_buffer);
398
399
0
  *state.whole = state.buffer;
400
0
  *whole_size = state.size;
401
402
0
  return DWFL_E_NOERROR;
403
0
}