Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/tvbuff_lz77.c
Line
Count
Source
1
/*
2
 * Decompression code for Plain LZ77. This encoding is used by
3
 * Microsoft in various file formats and protocols including SMB3.
4
 *
5
 * See MS-XCA.
6
 *
7
 * Copyright (C) 2019  Aurélien Aptel
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include <glib.h>
13
#include <epan/exceptions.h>
14
#include <epan/tvbuff.h>
15
#include <epan/wmem_scopes.h>
16
17
0
#define MAX_INPUT_SIZE (16*1024*1024) /* 16MB */
18
19
static bool do_uncompress(tvbuff_t *tvb, int offset, int in_size,
20
            wmem_array_t *obuf)
21
0
{
22
0
  unsigned buf_flags = 0, buf_flag_count = 0;
23
0
  int in_off = 0;
24
0
  int last_length_half_byte = 0;
25
0
  unsigned match_bytes, match_len, match_off;
26
0
  unsigned i;
27
28
0
  while (1) {
29
0
    if (buf_flag_count == 0) {
30
0
      buf_flags = tvb_get_letohl(tvb, offset+in_off);
31
0
      in_off += 4;
32
0
      buf_flag_count = 32;
33
0
    }
34
0
    buf_flag_count--;
35
0
    if ((buf_flags & (1u << buf_flag_count)) == 0) {
36
0
      uint8_t v = tvb_get_uint8(tvb, offset+in_off);
37
0
      wmem_array_append_one(obuf, v);
38
0
      in_off++;
39
0
    } else {
40
0
      if (in_off == in_size)
41
0
        return true;
42
0
      match_bytes = tvb_get_letohs(tvb, offset+in_off);
43
0
      in_off += 2;
44
0
      match_len = match_bytes % 8;
45
0
      match_off = (match_bytes/8) + 1;
46
0
      if (match_len == 7) {
47
0
        if (last_length_half_byte == 0) {
48
0
          match_len = tvb_get_uint8(tvb, offset+in_off);
49
0
          match_len = match_len % 16;
50
0
          last_length_half_byte = in_off;
51
0
          in_off++;
52
0
        } else {
53
0
          match_len = tvb_get_uint8(tvb, offset+last_length_half_byte);
54
0
          match_len = match_len / 16;
55
0
          last_length_half_byte = 0;
56
0
        }
57
0
        if (match_len == 15) {
58
0
          match_len = tvb_get_uint8(tvb, offset+in_off);
59
0
          in_off++;
60
0
          if (match_len == 255) {
61
0
            match_len = tvb_get_letohs(tvb, offset+in_off);
62
0
            in_off += 2;
63
0
            if (match_len == 0) {
64
0
              match_len = tvb_get_letohl(tvb, offset+in_off);
65
0
              in_off += 4;
66
0
            }
67
0
            if (match_len < 15+7)
68
0
              return false;
69
0
            match_len -= (15 + 7);
70
0
          }
71
0
          match_len += 15;
72
0
        }
73
0
        match_len += 7;
74
0
      }
75
      /* XXX - We could instead fail inside the loops, e.g.
76
       * testing wmem_array_get_count(obuf) > MAX_INPUT_SIZE,
77
       * and return what we could decompress in a tvb with
78
       * reported length greater than captured length. */
79
0
      if (match_len > MAX_INPUT_SIZE)
80
0
        return false;
81
0
      match_len += 3;
82
                        /* We can copy up to match_off bytes at a time.
83
                         * (The overlap handling is *not* like memmove,
84
                         * see [MS-XCA] 2.4.4.) */
85
                        /* wmem_array_get_count only increases, so we only need
86
                         * test this once. */
87
0
      if (match_off > wmem_array_get_count(obuf))
88
0
        return false;
89
0
      uint8_t *src;
90
0
      ws_assert(match_off != 0); // Guaranteed by line 45
91
      /* Must call grow first, to realloc the array first
92
       * if needed and avoid invalidating pointers returned
93
       * from wmem_array_index when appending. */
94
0
                        if (!wmem_array_grow(obuf, match_len))
95
0
                                return false;
96
0
      for (i = 0; i < match_len / match_off; i++) {
97
0
        src = wmem_array_index(obuf, wmem_array_get_count(obuf) - match_off);
98
0
        wmem_array_append(obuf, src, match_off);
99
0
      }
100
0
      for (i *= match_off; i < match_len; i++) {
101
0
        src = wmem_array_index(obuf, wmem_array_get_count(obuf) - match_off);
102
0
        wmem_array_append(obuf, src, 1);
103
0
      }
104
0
    }
105
0
  }
106
107
0
  return true;
108
0
}
109
110
tvbuff_t *
111
tvb_uncompress_lz77(tvbuff_t *tvb, const unsigned offset, unsigned in_size)
112
0
{
113
0
  volatile bool ok = false;
114
0
  wmem_allocator_t *pool;
115
0
  wmem_array_t *obuf;
116
0
  tvbuff_t *out;
117
118
0
  if (!tvb)
119
0
    return NULL;
120
121
0
  if (!in_size || in_size > MAX_INPUT_SIZE)
122
0
    return NULL;
123
124
0
  pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
125
0
  obuf = wmem_array_sized_new(pool, 1, in_size*2);
126
127
0
  TRY {
128
0
                ok = do_uncompress(tvb, offset, in_size, obuf);
129
0
  } CATCH_ALL {
130
0
    ok = false;
131
0
  }
132
0
  ENDTRY;
133
134
0
  if (ok) {
135
    /*
136
     * Cannot pass a tvb free callback that frees the wmem
137
     * pool, so we make an extra copy that uses bare
138
     * pointers. This could be optimized if tvb API had a
139
     * free pool callback of some sort.
140
     *
141
     * XXX - Maybe a tvb_set_free_cb_with_data or similar
142
     * that takes functions that take a void* userdata
143
     * parameter?
144
     */
145
0
    unsigned size = wmem_array_get_count(obuf);
146
0
    uint8_t *p = (uint8_t *)g_malloc(size);
147
0
    memcpy(p, wmem_array_get_raw(obuf), size);
148
0
    out = tvb_new_real_data(p, size, size);
149
0
    tvb_set_free_cb(out, g_free);
150
0
  } else {
151
0
    out = NULL;
152
0
  }
153
154
0
  wmem_destroy_allocator(pool);
155
156
0
  return out;
157
0
}
158
159
tvbuff_t *
160
tvb_child_uncompress_lz77(tvbuff_t *parent, tvbuff_t *tvb, const unsigned offset, unsigned in_size)
161
0
{
162
0
  tvbuff_t *new_tvb = tvb_uncompress_lz77(tvb, offset, in_size);
163
0
  if (new_tvb)
164
0
    tvb_set_child_real_data_tvbuff(parent, new_tvb);
165
0
  return new_tvb;
166
0
}
167
168
169
/*
170
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
171
 *
172
 * Local variables:
173
 * c-basic-offset: 8
174
 * tab-width: 8
175
 * indent-tabs-mode: t
176
 * End:
177
 *
178
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
179
 * :indentSize=8:tabSize=8:noTabs=false:
180
 */