Coverage Report

Created: 2026-07-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/tvbuff_zlib.c
Line
Count
Source
1
/* tvbuff_zlib.c
2
 *
3
 * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include <config.h>
13
0
#define WS_LOG_DOMAIN LOG_DOMAIN_EPAN
14
15
#include <glib.h>
16
17
#include <string.h>
18
#include <wsutil/glib-compat.h>
19
#include <wsutil/zlib_compat.h>
20
21
#include "tvbuff.h"
22
#include <wsutil/wslog.h>
23
24
#ifdef USE_ZLIB_OR_ZLIBNG
25
/*
26
 * Uncompresses a zlib compressed packet inside a message of tvb at offset with
27
 * length comprlen.  Returns an uncompressed tvbuffer if uncompression
28
 * succeeded or NULL if uncompression failed.
29
 */
30
#define TVB_Z_MIN_BUFSIZ 32768
31
0
#define TVB_Z_MAX_BUFSIZ 1048576 * 10
32
33
tvbuff_t *
34
tvb_uncompress_zlib(tvbuff_t *tvb, const unsigned offset, unsigned comprlen)
35
455
{
36
455
  int        err;
37
  /* bytes_out is an unsigned because tvb_new_real_data does not accept
38
   * more than an unsigned. */
39
455
  unsigned   bytes_out      = 0;
40
455
  uint8_t   *compr;
41
455
  tvbuff_t  *uncompr_tvb    = NULL;
42
455
  zlib_stream strm          = {0};
43
455
  Bytef     *strmbuf;
44
455
  unsigned   inits_done     = 0;
45
455
  int        wbits          = MAX_WBITS;
46
455
  uint8_t   *next;
47
455
  unsigned   bufsiz;
48
455
  unsigned   inflate_passes = 0;
49
455
  unsigned   bytes_in       = tvb_captured_length_remaining(tvb, offset);
50
51
455
  if (tvb == NULL || comprlen <= 0) {
52
56
    return NULL;
53
56
  }
54
55
399
  compr = (uint8_t *)tvb_memdup(NULL, tvb, offset, comprlen);
56
399
  if (compr == NULL) {
57
0
    return NULL;
58
0
  }
59
60
  /*
61
   * Assume that the uncompressed data is at least twice as big as
62
   * the compressed size.
63
   */
64
399
  if (ckd_mul(&bufsiz, bytes_in, 2)) {
65
0
    bufsiz = TVB_Z_MAX_BUFSIZ;
66
399
  } else {
67
399
    bufsiz = CLAMP(bufsiz, TVB_Z_MIN_BUFSIZ, TVB_Z_MAX_BUFSIZ);
68
399
  }
69
70
399
  ws_debug("bufsiz: %u bytes\n", bufsiz);
71
72
399
  next = compr;
73
74
399
  strm.next_in   = next;
75
399
  strm.avail_in  = comprlen;
76
77
399
  strmbuf         = (Bytef *)g_malloc(bufsiz);
78
79
399
  err = ZLIB_PREFIX(inflateInit2)(&strm, wbits);
80
399
  inits_done = 1;
81
399
  if (err != Z_OK) {
82
0
    ZLIB_PREFIX(inflateEnd)(&strm);
83
0
    goto exit_err;
84
0
  }
85
86
1.15k
  while (1) {
87
1.13k
    strm.next_out  = &strmbuf[bytes_out];
88
1.13k
    strm.avail_out = bufsiz;
89
90
1.13k
    err = ZLIB_PREFIX(inflate)(&strm, Z_SYNC_FLUSH);
91
92
1.13k
    if (err == Z_OK || err == Z_STREAM_END) {
93
311
      unsigned bytes_pass = bufsiz - strm.avail_out;
94
95
      // This is an unsigned long, but won't overflow even
96
      // on platforms where that is 32-bit, because bufsize
97
      // is clamped, if we break when it reaches INT_MAX.
98
311
      if (strm.total_out > INT_MAX) {
99
        // Qt uses signed ints in various classes, so
100
        // the GUI can't really handle anything
101
0
        ws_debug("overflow, returning what we have");
102
0
        ZLIB_PREFIX(inflateEnd)(&strm);
103
0
        break;
104
0
      }
105
106
      /* Use this for a workaround for bug #6480
107
       * (https://gitlab.com/wireshark/wireshark/-/issues/6480)
108
       * When a zero length payload was compressed into 20 bytes
109
       * bytes we want to return a tvb with 0 length instead
110
       * of NULL. */
111
311
      ++inflate_passes;
112
113
311
      bytes_out += bytes_pass;
114
311
      ws_assert(bytes_out == strm.total_out);
115
116
311
      if (strm.avail_out == 0) {
117
0
        strmbuf = (uint8_t*)g_realloc(strmbuf, bytes_out + bufsiz);
118
0
      }
119
120
311
      if (err == Z_STREAM_END) {
121
14
        ZLIB_PREFIX(inflateEnd)(&strm);
122
14
        break;
123
14
      }
124
825
    } else if (err == Z_BUF_ERROR) {
125
      /*
126
       * It's possible that not enough frames were captured
127
       * to decompress this fully, so return what we've done
128
       * so far, if any.
129
       */
130
297
      ZLIB_PREFIX(inflateEnd)(&strm);
131
132
297
      if (inflate_passes) {
133
297
        break;
134
297
      } else {
135
0
        goto exit_err;
136
0
      }
137
138
528
    } else if (err == Z_DATA_ERROR && inits_done == 1
139
334
      && inflate_passes == 0 && comprlen >= 2 &&
140
334
      (*compr  == 0x1f) && (*(compr + 1) == 0x8b)) {
141
      /*
142
       * inflate() is supposed to handle both gzip and deflate
143
       * streams automatically, but in reality it doesn't
144
       * seem to handle either (at least not within the
145
       * context of an HTTP response.)  We have to try
146
       * several tweaks, depending on the type of data and
147
       * version of the library installed.
148
       */
149
150
      /*
151
       * Gzip file format.  Skip past the header, since the
152
       * fix to make it work (setting windowBits to 31)
153
       * doesn't work with all versions of the library.
154
       */
155
0
      Bytef *c = compr + 2;
156
0
      Bytef  flags = 0;
157
158
      /* we read two bytes already (0x1f, 0x8b) and
159
         need at least Z_DEFLATED, 1 byte flags, 4
160
         bytes MTIME, 1 byte XFL, 1 byte OS */
161
0
      if (comprlen < 10 || *c != Z_DEFLATED) {
162
0
        ZLIB_PREFIX(inflateEnd)(&strm);
163
0
        goto exit_err;
164
0
      }
165
166
0
      c++;
167
0
      flags = *c;
168
0
      c++;
169
170
      /* Skip past the MTIME (4 bytes),
171
         XFL, and OS fields (1 byte each). */
172
0
      c += 6;
173
174
0
      if (flags & (1 << 2)) {
175
        /* An Extra field is present. It
176
           consists of 2 bytes xsize and xsize
177
           bytes of data.
178
           Read byte-by-byte (least significant
179
           byte first) to make sure we abort
180
           cleanly when the xsize is truncated
181
           after the first byte. */
182
0
        uint16_t xsize = 0;
183
184
0
        if ((unsigned)(c-compr) < comprlen) {
185
0
          xsize += *c;
186
0
          c++;
187
0
        }
188
0
        if ((unsigned)(c-compr) < comprlen) {
189
0
          xsize += *c << 8;
190
0
          c++;
191
0
        }
192
193
0
        c += xsize;
194
0
      }
195
196
0
      if (flags & (1 << 3)) {
197
        /* A null terminated filename */
198
199
0
        while ((unsigned)(c - compr) < comprlen && *c != '\0') {
200
0
          c++;
201
0
        }
202
203
0
        c++;
204
0
      }
205
206
0
      if (flags & (1 << 4)) {
207
        /* A null terminated comment */
208
209
0
        while ((unsigned)(c - compr) < comprlen && *c != '\0') {
210
0
          c++;
211
0
        }
212
213
0
        c++;
214
0
      }
215
216
217
0
      if ((unsigned)(c - compr) > comprlen) {
218
0
        ZLIB_PREFIX(inflateEnd)(&strm);
219
0
        goto exit_err;
220
0
      }
221
      /* Drop gzip header */
222
0
      comprlen -= (unsigned)(c - compr);
223
0
      next = c;
224
225
0
      ZLIB_PREFIX(inflateReset)(&strm);
226
0
      strm.next_in   = next;
227
0
      strm.avail_in  = comprlen;
228
229
0
      ZLIB_PREFIX(inflateEnd)(&strm);
230
0
      err = ZLIB_PREFIX(inflateInit2)(&strm, wbits);
231
0
      inits_done++;
232
0
      if (err != Z_OK) {
233
0
        goto exit_err;
234
0
      }
235
528
    } else if (err == Z_DATA_ERROR && inflate_passes == 0 &&
236
526
      inits_done <= 3) {
237
238
      /*
239
       * Re-init the stream with a negative
240
       * MAX_WBITS. This is necessary due to
241
       * some servers (Apache) not sending
242
       * the deflate header with the
243
       * content-encoded response.
244
       */
245
462
      wbits = -MAX_WBITS;
246
247
462
      ZLIB_PREFIX(inflateReset)(&strm);
248
249
462
      strm.next_in   = next;
250
462
      strm.avail_in  = comprlen;
251
252
462
      ZLIB_PREFIX(inflateEnd)(&strm);
253
462
      strm.next_out  = strmbuf;
254
462
      strm.avail_out = bufsiz;
255
256
462
      err = ZLIB_PREFIX(inflateInit2)(&strm, wbits);
257
258
462
      inits_done++;
259
260
462
      if (err != Z_OK) {
261
0
        goto exit_err;
262
0
      }
263
462
    } else {
264
66
      ZLIB_PREFIX(inflateEnd)(&strm);
265
266
66
      if (!inflate_passes) {
267
66
        goto exit_err;
268
66
      }
269
270
0
      break;
271
66
    }
272
1.13k
  }
273
274
333
  ws_debug("inflate() total passes: %u\n", inflate_passes);
275
333
  ws_debug("bytes  in: %u\nbytes out: %u\n\n", bytes_in, bytes_out);
276
277
333
  if (inflate_passes) {
278
    /* g_realloc(..., 0) is well-defined, returns NULL. */
279
311
    strmbuf = (uint8_t*)g_realloc(strmbuf, bytes_out);
280
311
    uncompr_tvb = tvb_new_real_data(strmbuf, bytes_out, bytes_out);
281
311
    tvb_set_free_cb(uncompr_tvb, g_free);
282
311
    wmem_free(NULL, compr);
283
311
    return uncompr_tvb;
284
311
  }
285
286
66
exit_err:
287
66
  wmem_free(NULL, compr);
288
66
  g_free(strmbuf);
289
66
  return NULL;
290
333
}
291
#else /* USE_ZLIB_OR_ZLIBNG */
292
tvbuff_t *
293
tvb_uncompress_zlib(tvbuff_t *tvb _U_, const unsigned offset _U_, unsigned comprlen _U_)
294
{
295
  return NULL;
296
}
297
#endif /* USE_ZLIB_OR_ZLIBNG */
298
299
tvbuff_t *
300
tvb_child_uncompress_zlib(tvbuff_t *parent, tvbuff_t *tvb, const unsigned offset, unsigned comprlen)
301
455
{
302
455
  tvbuff_t *new_tvb = tvb_uncompress_zlib(tvb, offset, comprlen);
303
455
  if (new_tvb)
304
311
    tvb_set_child_real_data_tvbuff (parent, new_tvb);
305
455
  return new_tvb;
306
455
}
307
308
/*
309
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
310
 *
311
 * Local variables:
312
 * c-basic-offset: 8
313
 * tab-width: 8
314
 * indent-tabs-mode: t
315
 * End:
316
 *
317
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
318
 * :indentSize=8:tabSize=8:noTabs=false:
319
 */