### Bug Summary

File: | builds/wireshark/wireshark/epan/tvbuff_zlib.c
Warning: | line 280, column 15 Potential leak of memory pointed to by 'strmbuf'

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tvbuff_zlib.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan -isystem /builds/wireshark/wireshark/build/epan -isystem /usr/include/mit-krb5 -isystem /usr/include/lua5.5 -isystem /usr/include/libxml2 -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D epan_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-07-18-100301-3641-1 -x c /builds/wireshark/wireshark/epan/tvbuff_zlib.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

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 | #define WS_LOG_DOMAIN"Epan" LOG_DOMAIN_EPAN"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_BUFSIZ32768 32768
31 | #define TVB_Z_MAX_BUFSIZ1048576 * 10 1048576 * 10
32 |
33 | tvbuff_t *
34 | tvb_uncompress_zlib(tvbuff_t *tvb, const unsigned offset, unsigned comprlen)
35 | {
36 | int err;
37 | /* bytes_out is an unsigned because tvb_new_real_data does not accept
38 | * more than an unsigned. */
39 | unsigned bytes_out = 0;
40 | uint8_t *compr;
41 | tvbuff_t *uncompr_tvb = NULL((void*)0);
42 | zlib_stream strm = {0};
43 | Bytef *strmbuf;
44 | unsigned inits_done = 0;
45 | int wbits = MAX_WBITS15;
46 | uint8_t *next;
47 | unsigned bufsiz;
48 | unsigned inflate_passes = 0;
49 | unsigned bytes_in = tvb_captured_length_remaining(tvb, offset);
50 |
51 | if (tvb == NULL((void*)0) || comprlen <= 0) {
2 | ← | Assuming 'tvb' is not equal to NULL | →
3 | ← | Assuming 'comprlen' is > 0 | →
4 | ← | Taking false branch | →
52 | return NULL((void*)0);
53 | }
54 |
55 | compr = (uint8_t *)tvb_memdup(NULL((void*)0), tvb, offset, comprlen);
56 | if (compr == NULL((void*)0)) {
5 | ← | Assuming 'compr' is not equal to NULL | →
6 | ← | Taking false branch | →
57 | return NULL((void*)0);
58 | }
59 |
60 | /*
61 | * Assume that the uncompressed data is at least twice as big as
62 | * the compressed size.
63 | */
64 | if (ckd_mul(&bufsiz, bytes_in, 2)__builtin_mul_overflow((bytes_in), (2), (&bufsiz))) {
7 | ← | Taking true branch | →
65 | bufsiz = TVB_Z_MAX_BUFSIZ1048576 * 10;
66 | } else {
67 | bufsiz = CLAMP(bufsiz, TVB_Z_MIN_BUFSIZ, TVB_Z_MAX_BUFSIZ)(((bufsiz) > (1048576 * 10)) ? (1048576 * 10) : (((bufsiz) < (32768)) ? (32768) : (bufsiz)));
68 | }
69 |
70 | ws_debug("bufsiz: %u bytes\n", bufsiz)do { if (1) { ws_log_full("Epan", LOG_LEVEL_DEBUG, "epan/tvbuff_zlib.c" , 70, __func__, "bufsiz: %u bytes\n", bufsiz); } } while (0);
8 | ← | Taking true branch | →
9 | ← | Loop condition is false. Exiting loop | →
71 |
72 | next = compr;
73 |
74 | strm.next_in = next;
75 | strm.avail_in = comprlen;
76 |
77 | strmbuf = (Bytef *)g_malloc(bufsiz);
78 |
79 | err = ZLIB_PREFIX(inflateInit2)(&strm, wbits)inflateInit2_((&strm), (wbits), "1.3.1", (int)sizeof(z_stream ));
80 | inits_done = 1;
81 | if (err != Z_OK0) {
10 | ← | Assuming 'err' is equal to Z_OK | →
11 | ← | Taking false branch | →
82 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
83 | goto exit_err;
84 | }
85 |
86 | while (1) {
12 | ← | Loop condition is true. Entering loop body | →
87 | strm.next_out = &strmbuf[bytes_out];
88 | strm.avail_out = bufsiz;
89 |
90 | err = ZLIB_PREFIX(inflate)inflate(&strm, Z_SYNC_FLUSH2);
91 |
92 | if (err == Z_OK0 || err == Z_STREAM_END1) {
13 | ← | Assuming 'err' is not equal to Z_OK | →
14 | ← | Assuming 'err' is equal to Z_STREAM_END | →
15 | ← | Taking true branch | →
93 | 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 | if (strm.total_out > INT_MAX2147483647) {
16 | ← | Assuming field 'total_out' is <= INT_MAX | →
17 | ← | Taking false branch | →
99 | // Qt uses signed ints in various classes, so
100 | // the GUI can't really handle anything
101 | ws_debug("overflow, returning what we have")do { if (1) { ws_log_full("Epan", LOG_LEVEL_DEBUG, "epan/tvbuff_zlib.c" , 101, __func__, "overflow, returning what we have"); } } while (0);
102 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
103 | break;
104 | }
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 | ++inflate_passes;
112 |
113 | bytes_out += bytes_pass;
114 | ws_assert(bytes_out == strm.total_out)do { if ((1) && !(bytes_out == strm.total_out)) ws_log_fatal_full ("Epan", LOG_LEVEL_ERROR, "epan/tvbuff_zlib.c", 114, __func__ , "assertion failed: %s", "bytes_out == strm.total_out"); } while (0);
18 | ← | Assuming 'bytes_out' is equal to field 'total_out' | →
19 | ← | Taking false branch | →
20 | ← | Loop condition is false. Exiting loop | →
115 |
116 | if (strm.avail_out == 0) {
21 | ← | Assuming field 'avail_out' is not equal to 0 | →
22 | ← | Taking false branch | →
117 | strmbuf = (uint8_t*)g_realloc(strmbuf, bytes_out + bufsiz);
118 | }
119 |
120 | if (err
22.1 | 'err' is equal to Z_STREAM_END
== Z_STREAM_END1) {
23 | ← | Taking true branch | →
121 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
122 | break;
24 | ← | Execution continues on line 274 | →
123 | }
124 | } else if (err == Z_BUF_ERROR(-5)) {
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 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
131 |
132 | if (inflate_passes) {
133 | break;
134 | } else {
135 | goto exit_err;
136 | }
137 |
138 | } else if (err == Z_DATA_ERROR(-3) && inits_done == 1
139 | && inflate_passes == 0 && comprlen >= 2 &&
140 | (*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 | Bytef *c = compr + 2;
156 | 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 | if (comprlen < 10 || *c != Z_DEFLATED8) {
162 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
163 | goto exit_err;
164 | }
165 |
166 | c++;
167 | flags = *c;
168 | c++;
169 |
170 | /* Skip past the MTIME (4 bytes),
171 | XFL, and OS fields (1 byte each). */
172 | c += 6;
173 |
174 | 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 | uint16_t xsize = 0;
183 |
184 | if ((unsigned)(c-compr) < comprlen) {
185 | xsize += *c;
186 | c++;
187 | }
188 | if ((unsigned)(c-compr) < comprlen) {
189 | xsize += *c << 8;
190 | c++;
191 | }
192 |
193 | c += xsize;
194 | }
195 |
196 | if (flags & (1 << 3)) {
197 | /* A null terminated filename */
198 |
199 | while ((unsigned)(c - compr) < comprlen && *c != '\0') {
200 | c++;
201 | }
202 |
203 | c++;
204 | }
205 |
206 | if (flags & (1 << 4)) {
207 | /* A null terminated comment */
208 |
209 | while ((unsigned)(c - compr) < comprlen && *c != '\0') {
210 | c++;
211 | }
212 |
213 | c++;
214 | }
215 |
216 |
217 | if ((unsigned)(c - compr) > comprlen) {
218 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
219 | goto exit_err;
220 | }
221 | /* Drop gzip header */
222 | comprlen -= (unsigned)(c - compr);
223 | next = c;
224 |
225 | ZLIB_PREFIX(inflateReset)inflateReset(&strm);
226 | strm.next_in = next;
227 | strm.avail_in = comprlen;
228 |
229 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
230 | err = ZLIB_PREFIX(inflateInit2)(&strm, wbits)inflateInit2_((&strm), (wbits), "1.3.1", (int)sizeof(z_stream ));
231 | inits_done++;
232 | if (err != Z_OK0) {
233 | goto exit_err;
234 | }
235 | } else if (err == Z_DATA_ERROR(-3) && inflate_passes == 0 &&
236 | 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 | wbits = -MAX_WBITS15;
246 |
247 | ZLIB_PREFIX(inflateReset)inflateReset(&strm);
248 |
249 | strm.next_in = next;
250 | strm.avail_in = comprlen;
251 |
252 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
253 | strm.next_out = strmbuf;
254 | strm.avail_out = bufsiz;
255 |
256 | err = ZLIB_PREFIX(inflateInit2)(&strm, wbits)inflateInit2_((&strm), (wbits), "1.3.1", (int)sizeof(z_stream ));
257 |
258 | inits_done++;
259 |
260 | if (err != Z_OK0) {
261 | goto exit_err;
262 | }
263 | } else {
264 | ZLIB_PREFIX(inflateEnd)inflateEnd(&strm);
265 |
266 | if (!inflate_passes) {
267 | goto exit_err;
268 | }
269 |
270 | break;
271 | }
272 | }
273 |
274 | ws_debug("inflate() total passes: %u\n", inflate_passes)do { if (1) { ws_log_full("Epan", LOG_LEVEL_DEBUG, "epan/tvbuff_zlib.c" , 274, __func__, "inflate() total passes: %u\n", inflate_passes ); } } while (0);
25 | ← | Taking true branch | →
26 | ← | Loop condition is false. Exiting loop | →
275 | ws_debug("bytes in: %u\nbytes out: %u\n\n", bytes_in, bytes_out)do { if (1) { ws_log_full("Epan", LOG_LEVEL_DEBUG, "epan/tvbuff_zlib.c" , 275, __func__, "bytes in: %u\nbytes out: %u\n\n", bytes_in , bytes_out); } } while (0);
27 | ← | Taking true branch | →
28 | ← | Loop condition is false. Exiting loop | →
276 |
277 | if (inflate_passes
28.1 | 'inflate_passes' is 1
) {
29 | ← | Taking true branch | →
278 | /* g_realloc(..., 0) is well-defined, returns NULL. */
279 | strmbuf = (uint8_t*)g_realloc(strmbuf, bytes_out);
30 | ← | Memory is allocated | →
280 | uncompr_tvb = tvb_new_real_data(strmbuf, bytes_out, bytes_out);
31 | ← | Potential leak of memory pointed to by 'strmbuf'
281 | tvb_set_free_cb(uncompr_tvb, g_free);
282 | wmem_free(NULL((void*)0), compr);
283 | return uncompr_tvb;
284 | }
285 |
286 | exit_err:
287 | wmem_free(NULL((void*)0), compr);
288 | g_free(strmbuf)(__builtin_object_size ((strmbuf), 0) != ((size_t) - 1)) ? g_free_sized (strmbuf, __builtin_object_size ((strmbuf), 0)) : (g_free) ( strmbuf);
289 | return NULL((void*)0);
290 | }
291 | #else /* USE_ZLIB_OR_ZLIBNG */
292 | tvbuff_t *
293 | tvb_uncompress_zlib(tvbuff_t *tvb _U___attribute__((unused)), const unsigned offset _U___attribute__((unused)), unsigned comprlen _U___attribute__((unused)))
294 | {
295 | return NULL((void*)0);
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 | {
302 | tvbuff_t *new_tvb = tvb_uncompress_zlib(tvb, offset, comprlen);
1 | Calling 'tvb_uncompress_zlib' | →
303 | if (new_tvb)
304 | tvb_set_child_real_data_tvbuff (parent, new_tvb);
305 | return new_tvb;
306 | }
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 | */