### Bug Summary

File: | builds/wireshark/wireshark/epan/tvbuff_lz77huff.c
Warning: | line 402, column 7 Potential leak of memory pointed to by 'p'

### 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_lz77huff.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-21/lib/clang/21 -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.4 -isystem /usr/include/libxml2 -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-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-05-20-100323-3659-1 -x c /builds/wireshark/wireshark/epan/tvbuff_lz77huff.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 | /*
2 | * Decompression code for LZ77+Huffman. This encoding is used by
3 | * Microsoft in various file formats and protocols including SMB3.
4 | *
5 | * See MS-XCA.
6 | *
7 | * Initial code from Samba re-licensed with Samuel's permission.
8 | * Copyright (C) Samuel Cabrero 2017
9 | *
10 | * Glib-ification, extra error-checking and WS integration
11 | * Copyright (C) Aurélien Aptel 2019
12 | *
13 | * Wireshark - Network traffic analyzer
14 | * By Gerald Combs <gerald@wireshark.org>
15 | * Copyright 1998 Gerald Combs
16 | *
17 | * SPDX-License-Identifier: GPL-2.0-or-later
18 | */
19 |
20 | #include <glib.h>
21 | #include <stdlib.h> /* qsort */
22 | #include <epan/exceptions.h>
23 | #include <epan/tvbuff.h>
24 | #include <epan/wmem_scopes.h>
25 |
26 | #define MAX_INPUT_SIZE(16*1024*1024) (16*1024*1024) /* 16MB */
27 |
28 | #define TREE_SIZE1024 1024
29 | #define ENCODED_TREE_SIZE256 256
30 | #define SYMBOL_INFO_SIZE(2*256) (2*ENCODED_TREE_SIZE256)
31 |
32 | struct input {
33 | tvbuff_t *tvb;
34 | int offset;
35 | size_t size;
36 | };
37 |
38 | /**
39 | * Represents a node in a Huffman prefix code tree
40 | */
41 | struct prefix_code_node {
42 | /* Stores the symbol encoded by this node in the prefix code tree */
43 | uint16_t symbol;
44 |
45 | /* Indicates whether this node is a leaf in the tree */
46 | uint8_t leaf;
47 |
48 | /*
49 | * Points to the node's two children. Values are indexes in
50 | * the tree node array. The value -1 is used to indicate that
51 | * a particular child does not exist
52 | */
53 | int16_t child[2];
54 | };
55 |
56 | /**
57 | * Represent information about a Huffman-encoded symbol
58 | */
59 | struct prefix_code_symbol {
60 | /* Stores the symbol */
61 | uint16_t symbol;
62 |
63 | /* Stores the symbol’s Huffman prefix code length */
64 | uint16_t length;
65 | };
66 |
67 | /**
68 | * Represent a byte array as a bit string from which individual bits can
69 | * be read
70 | */
71 | struct bitstring {
72 | /* The byte array */
73 | const struct input *input;
74 |
75 | /* The index in source from which the next set of bits will be pulled
76 | * when the bits in mask have been consumed */
77 | uint32_t bitstring_index;
78 |
79 | /* Stores the next bits to be consumed in the bit string */
80 | uint32_t mask;
81 |
82 | /* Stores the number of bits in mask that remain to be consumed */
83 | int32_t bits;
84 | };
85 |
86 | struct hf_tree {
87 | struct prefix_code_node *root;
88 | struct prefix_code_node nodes[TREE_SIZE1024];
89 | };
90 |
91 | static bool_Bool is_node_valid(struct hf_tree *tree, struct prefix_code_node *node)
92 | {
93 | return (node && node >= tree->nodes && node < tree->nodes + TREE_SIZE1024);
94 | }
95 |
96 | /**
97 | * Links a symbol's prefix_code_node into its correct position in a Huffman
98 | * prefix code tree
99 | */
100 | static int prefix_code_tree_add_leaf(struct hf_tree *tree,
101 | uint32_t leaf_index,
102 | uint32_t mask,
103 | uint32_t bits,
104 | uint32_t *out_index)
105 | {
106 | struct prefix_code_node *node = &tree->nodes[0];
107 | uint32_t i = leaf_index + 1;
108 | uint32_t child_index;
109 |
110 | if (leaf_index >= TREE_SIZE1024)
111 | return -1;
112 |
113 | while (bits > 1) {
114 | bits = bits - 1;
115 | child_index = (mask >> bits) & 1;
116 | if (node->child[child_index] < 0) {
117 | if (i >= TREE_SIZE1024)
118 | return -1;
119 | node->child[child_index] = i;
120 | tree->nodes[i].leaf = false0;
121 | i = i + 1;
122 | }
123 | node = tree->nodes + node->child[child_index];
124 | if (!is_node_valid(tree, node))
125 | return -1;
126 | }
127 |
128 | node->child[mask & 1] = leaf_index;
129 |
130 | *out_index = i;
131 | return 0;
132 | }
133 |
134 | /**
135 | * Determines the sort order of one prefix_code_symbol relative to another
136 | */
137 | static int compare_symbols(const void *ve1, const void *ve2)
138 | {
139 | const struct prefix_code_symbol *e1 = (const struct prefix_code_symbol *)ve1;
140 | const struct prefix_code_symbol *e2 = (const struct prefix_code_symbol *)ve2;
141 |
142 | if (e1->length < e2->length)
143 | return -1;
144 | else if (e1->length > e2->length)
145 | return 1;
146 | else if (e1->symbol < e2->symbol)
147 | return -1;
148 | else if (e1->symbol > e2->symbol)
149 | return 1;
150 | else
151 | return 0;
152 | }
153 |
154 | /**
155 | * Rebuilds the Huffman prefix code tree that will be used to decode symbols
156 | * during decompression
157 | */
158 | static int PrefixCodeTreeRebuild( struct hf_tree *tree,
159 | const struct input *input)
160 | {
161 | struct prefix_code_symbol symbolInfo[SYMBOL_INFO_SIZE(2*256)];
162 | uint32_t i, j, mask, bits;
163 | int rc;
164 |
165 | for (i = 0; i < TREE_SIZE1024; i++) {
166 | tree->nodes[i].symbol = 0;
167 | tree->nodes[i].leaf = false0;
168 | tree->nodes[i].child[0] = -1;
169 | tree->nodes[i].child[1] = -1;
170 | }
171 |
172 | if (input->size < ENCODED_TREE_SIZE256)
173 | return -1;
174 |
175 | for (i = 0; i < ENCODED_TREE_SIZE256; i++) {
176 | symbolInfo[2*i].symbol = 2*i;
177 | symbolInfo[2*i].length = tvb_get_uint8(input->tvb, input->offset+i) & 15;
178 | symbolInfo[2*i+1].symbol = 2*i+1;
179 | symbolInfo[2*i+1].length = tvb_get_uint8(input->tvb, input->offset+i) >> 4;
180 | }
181 |
182 | qsort(symbolInfo, SYMBOL_INFO_SIZE(2*256), sizeof(symbolInfo[0]), compare_symbols);
183 |
184 | i = 0;
185 | while (i < SYMBOL_INFO_SIZE(2*256) && symbolInfo[i].length == 0) {
186 | i = i + 1;
187 | }
188 |
189 | mask = 0;
190 | bits = 1;
191 |
192 | tree->root = &tree->nodes[0];
193 | tree->root->leaf = false0;
194 |
195 | j = 1;
196 | for (; i < 512; i++) {
197 | //ws_assert(j < TREE_SIZE);
198 | if (j >= TREE_SIZE1024) {
199 | return -1;
200 | }
201 | tree->nodes[j].symbol = symbolInfo[i].symbol;
202 | tree->nodes[j].leaf = true1;
203 | mask <<= symbolInfo[i].length - bits;
204 | bits = symbolInfo[i].length;
205 | rc = prefix_code_tree_add_leaf(tree, j, mask, bits, &j);
206 | if (rc)
207 | return rc;
208 | mask += 1;
209 | }
210 |
211 | return 0;
212 | }
213 |
214 | /**
215 | * Initializes a bitstream data structure
216 | */
217 | static void bitstring_init(struct bitstring *bstr,
218 | const struct input *input,
219 | uint32_t bitstring_index)
220 | {
221 | bstr->mask = tvb_get_letohs(input->tvb, input->offset+bitstring_index);
222 | bstr->mask <<= sizeof(bstr->mask) * 8 - 16;
223 | bitstring_index += 2;
224 |
225 | bstr->mask += tvb_get_letohs(input->tvb, input->offset+bitstring_index);
226 | bitstring_index += 2;
227 |
228 | bstr->bits = 32;
229 | bstr->input = input;
230 | bstr->bitstring_index = bitstring_index;
231 | }
232 |
233 | /**
234 | * Returns the next n bits from the front of a bit string.
235 | */
236 | static uint32_t bitstring_lookup(struct bitstring *bstr, uint32_t n)
237 | {
238 | if (n == 0 || bstr->bits < 0 || n > (uint32_t)bstr->bits) {
239 | return 0;
240 | }
241 | return bstr->mask >> (sizeof(bstr->mask) * 8 - n);
242 | }
243 |
244 | /**
245 | * Advances the bit string's cursor by n bits.
246 | */
247 | static void bitstring_skip(struct bitstring *bstr, uint32_t n)
248 | {
249 | bstr->mask = bstr->mask << n;
250 | bstr->bits = bstr->bits - n;
251 |
252 | if (bstr->bits < 16) {
253 | bstr->mask += tvb_get_letohs(bstr->input->tvb,
254 | bstr->input->offset + bstr->bitstring_index)
255 | << (16 - bstr->bits);
256 | bstr->bitstring_index = bstr->bitstring_index + 2;
257 | bstr->bits = bstr->bits + 16;
258 | }
259 | }
260 |
261 | /**
262 | * Returns the symbol encoded by the next prefix code in a bit string.
263 | */
264 | static int prefix_code_tree_decode_symbol(struct hf_tree *tree,
265 | struct bitstring *bstr,
266 | uint32_t *out_symbol)
267 | {
268 | uint32_t bit;
269 | struct prefix_code_node *node = tree->root;
270 |
271 | do {
272 | bit = bitstring_lookup(bstr, 1);
273 | bitstring_skip(bstr, 1);
274 | node = tree->nodes + node->child[bit];
275 | if (!is_node_valid(tree, node))
276 | return -1;
277 | } while (node->leaf == false0);
278 |
279 | *out_symbol = node->symbol;
280 | return 0;
281 | }
282 |
283 | static bool_Bool do_uncompress(struct input *input,
284 | wmem_array_t *obuf)
285 | {
286 | uint32_t symbol;
287 | uint32_t length;
288 | unsigned match_offset;
289 | int rc;
290 | struct hf_tree tree = {0};
291 | struct bitstring bstr = {0};
292 |
293 | if (!input->tvb)
294 | return false0;
295 |
296 | if (!input->size || input->size > MAX_INPUT_SIZE(16*1024*1024))
297 | return false0;
298 |
299 | rc = PrefixCodeTreeRebuild(&tree, input);
300 | if (rc)
301 | return false0;
302 |
303 | bitstring_init(&bstr, input, ENCODED_TREE_SIZE256);
304 |
305 | while (1) {
306 | rc = prefix_code_tree_decode_symbol(&tree, &bstr, &symbol);
307 | if (rc < 0)
308 | return false0;
309 |
310 | if (symbol < 256) {
311 | uint8_t v = symbol & 0xFF;
312 | wmem_array_append_one(obuf, v)wmem_array_append((obuf), &(v), 1);
313 | } else {
314 | if (symbol == 256) {
315 | /* EOF symbol and everything consumed */
316 | if (bstr.bitstring_index == bstr.input->size) {
317 | return true1;
318 | }
319 | }
320 | symbol = symbol - 256;
321 | length = symbol & 0xF;
322 | symbol = symbol >> 4;
323 |
324 | match_offset = (1U << symbol) + bitstring_lookup(&bstr, symbol);
325 |
326 | if (length == 15) {
327 | if (bstr.bitstring_index >= bstr.input->size)
328 | return false0;
329 | length = tvb_get_uint8(bstr.input->tvb,
330 | bstr.input->offset+bstr.bitstring_index) + 15;
331 | bstr.bitstring_index += 1;
332 |
333 | if (length == 270) {
334 | if (bstr.bitstring_index+1 >= bstr.input->size)
335 | return false0;
336 | length = tvb_get_letohs(bstr.input->tvb, bstr.input->offset+bstr.bitstring_index);
337 | bstr.bitstring_index += 2;
338 | }
339 | }
340 |
341 | bitstring_skip(&bstr, symbol);
342 |
343 | length += 3;
344 | if (match_offset > wmem_array_get_count(obuf))
345 | return false0;
346 | ws_assert(match_offset != 0)do { if ((1) && !(match_offset != 0)) ws_log_fatal_full ("", LOG_LEVEL_ERROR, "epan/tvbuff_lz77huff.c", 346, __func__ , "assertion failed: %s", "match_offset != 0"); } while (0);
347 | /* Must call grow first, to realloc the array if needed
348 | * and avoid invalidating the pointers returned from
349 | * wmem_array_index. */
350 | if (!wmem_array_grow(obuf, length))
351 | return false0;
352 | uint8_t *src;
353 | unsigned i;
354 | for (i = 0; i < length / match_offset; i++) {
355 | src = wmem_array_index(obuf, wmem_array_get_count(obuf) - match_offset);
356 | wmem_array_append(obuf, src, match_offset);
357 | }
358 | for (i *= match_offset; i < length; i++) {
359 | src = wmem_array_index(obuf, wmem_array_get_count(obuf) - match_offset);
360 | wmem_array_append(obuf, src, 1);
361 | }
362 | }
363 | }
364 | return true1;
365 | }
366 |
367 | tvbuff_t *
368 | tvb_uncompress_lz77huff(tvbuff_t *tvb,
369 | const unsigned offset,
370 | unsigned input_size)
371 | {
372 | volatile bool_Bool ok;
373 | wmem_allocator_t *pool;
374 | wmem_array_t *obuf;
375 | tvbuff_t *out;
376 | struct input input = {
377 | .tvb = tvb,
378 | .offset = offset,
379 | .size = input_size
380 | };
381 |
382 | pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
383 | obuf = wmem_array_sized_new(pool, 1, input_size*2);
384 |
385 | TRY{ except_t *volatile exc; volatile int except_state = 0; static const except_id_t catch_spec[] = { { 1, 0 } }; { struct except_stacknode except_sn; struct except_catch except_ch; except_setup_try(& except_sn, &except_ch, catch_spec, 1); if (_setjmp (except_ch .except_jmp)) *(&exc) = &except_ch.except_obj; else * (&exc) = 0; if(except_state & 1) except_state |= 2; except_state &= ~1; if (except_state == 0 && exc == 0) {
2 | ← | Assuming the condition is false | →
3 | ← | Taking false branch | →
4 | ← | Taking false branch | →
5 | ← | Taking true branch | →
386 | ok = do_uncompress(&input, obuf);
387 | } CATCH_ALLif (except_state == 0 && exc != 0 && (except_state |=1)) {
388 | ok = false0;
389 | }
390 | ENDTRYif(!(except_state&1) && exc != 0) except_rethrow( exc); except_free(except_ch.except_obj.except_dyndata); except_pop (); };};
6 | ← | Taking false branch | →
391 |
392 | if (ok) {
7 | ← | Assuming 'ok' is true | →
8 | ← | Taking true branch | →
393 | /*
394 | * Cannot pass a tvb free callback that frees the wmem
395 | * pool, so we make an extra copy that uses bare
396 | * pointers. This could be optimized if tvb API had a
397 | * free pool callback of some sort.
398 | */
399 | unsigned size = wmem_array_get_count(obuf);
400 | uint8_t *p = (uint8_t *)g_malloc(size);
9 | ← | Memory is allocated | →
401 | memcpy(p, wmem_array_get_raw(obuf), size);
402 | out = tvb_new_real_data(p, size, size);
10 | ← | Potential leak of memory pointed to by 'p'
403 | tvb_set_free_cb(out, g_free);
404 | } else {
405 | out = NULL((void*)0);
406 | }
407 |
408 | wmem_destroy_allocator(pool);
409 |
410 | return out;
411 | }
412 |
413 | tvbuff_t *
414 | tvb_child_uncompress_lz77huff(tvbuff_t *parent, tvbuff_t *tvb, const unsigned offset, unsigned in_size)
415 | {
416 | tvbuff_t *new_tvb = tvb_uncompress_lz77huff(tvb, offset, in_size);
1 | Calling 'tvb_uncompress_lz77huff' | →
417 | if (new_tvb)
418 | tvb_set_child_real_data_tvbuff(parent, new_tvb);
419 | return new_tvb;
420 | }
421 |
422 | /*
423 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
424 | *
425 | * Local variables:
426 | * c-basic-offset: 8
427 | * tab-width: 8
428 | * indent-tabs-mode: t
429 | * End:
430 | *
431 | * vi: set shiftwidth=8 tabstop=8 noexpandtab:
432 | * :indentSize=8:tabSize=8:noTabs=false:
433 | */