Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/tvbuff_lznt1.c
Line
Count
Source
1
/*
2
 * Decompression code for LZNT1. 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
 * Wireshark - Network traffic analyzer
10
 * By Gerald Combs <gerald@wireshark.org>
11
 * Copyright 1998 Gerald Combs
12
 *
13
 * SPDX-License-Identifier: GPL-2.0-or-later
14
 */
15
16
#include <glib.h>
17
#include <epan/exceptions.h>
18
#include <epan/tvbuff.h>
19
#include <epan/wmem_scopes.h>
20
21
0
#define MAX_INPUT_SIZE (16*1024*1024) /* 16MB */
22
23
static bool
24
uncompress_chunk(tvbuff_t *tvb, int offset, unsigned in_size, wmem_array_t *obuf)
25
0
{
26
0
  unsigned in_off = 0, out_off = 0, out_start = 0;
27
0
  uint8_t flags;
28
0
  unsigned i, j, val, pos;
29
30
0
  out_start = wmem_array_get_count(obuf);
31
32
0
  while (in_off < in_size) {
33
0
    flags = tvb_get_uint8(tvb, offset+in_off);
34
0
    in_off++;
35
0
    for (i = 0; i < 8; i++) {
36
0
      if (0 == ((flags>>i)&1)) {
37
0
        val = tvb_get_uint8(tvb, offset+in_off);
38
0
        in_off++;
39
0
        wmem_array_append_one(obuf, val);
40
0
        out_off++;
41
0
      } else {
42
0
        unsigned f, l_mask = 0x0FFF, o_shift = 12;
43
0
        unsigned match_len, match_off;
44
45
0
        f = tvb_get_letohs(tvb, offset+in_off);
46
0
        in_off += 2;
47
                                /* Let M be the largest value in [4...12] such
48
                                 * that 2^{M-1} < U, or 4 if there is no such.
49
                                 * Equivalently, we can repeatedly divide U by
50
                                 * 2, but we need to subtract 1 first and test
51
                                 * vs a weak inequality in order to account for
52
                                 * truncation. (But don't overflow at U == 0.)
53
                                 */
54
0
                                if (out_off > 0) {
55
0
                                    pos = out_off-1;
56
0
                                    while (pos >= 0x10) {
57
0
                                            l_mask >>= 1;
58
0
                                            o_shift -= 1;
59
0
                                            pos >>= 1;
60
0
                                    }
61
0
                                }
62
63
0
        match_len = (f & l_mask) + 3;
64
0
        match_off = (f >> o_shift) + 1;
65
0
        for (j = 0; j < match_len; j++) {
66
0
          uint8_t byte;
67
0
          if (match_off > out_off)
68
0
            return false;
69
0
          if (wmem_array_try_index(obuf, out_start+out_off-match_off, &byte))
70
0
            return false;
71
0
          wmem_array_append_one(obuf, byte);
72
0
          out_off++;
73
0
        }
74
0
      }
75
0
      if (in_off == in_size) {
76
0
        goto out;
77
0
      }
78
0
    }
79
0
  }
80
0
out:
81
0
  return true;
82
0
}
83
84
static bool
85
do_uncompress(tvbuff_t *tvb, int offset, int in_size, wmem_array_t *obuf)
86
0
{
87
0
  int in_off = 0;
88
0
  uint32_t header, length, i;
89
0
  bool ok;
90
91
0
  if (!tvb)
92
0
    return false;
93
94
0
  if (!in_size || in_size > MAX_INPUT_SIZE)
95
0
    return false;
96
97
0
  while (in_off < in_size) {
98
0
    header = tvb_get_letohs(tvb, offset+in_off);
99
0
    in_off += 2;
100
0
    length = (header & 0x0FFF) + 1;
101
0
    if (!(header & 0x8000)) {
102
0
      for (i = 0; i < length; i++) {
103
0
        uint8_t v = tvb_get_uint8(tvb, offset+in_off);
104
0
        wmem_array_append_one(obuf, v);
105
0
        in_off++;
106
0
      }
107
0
    } else {
108
0
      ok = uncompress_chunk(tvb, offset + in_off, length, obuf);
109
0
      if (!ok)
110
0
        return false;
111
0
      in_off += length;
112
0
    }
113
0
  }
114
0
  return true;
115
0
}
116
117
tvbuff_t *
118
tvb_uncompress_lznt1(tvbuff_t *tvb, const unsigned offset, unsigned in_size)
119
0
{
120
0
  volatile bool ok = false;
121
0
  wmem_allocator_t *pool;
122
0
  wmem_array_t *obuf;
123
0
  tvbuff_t *out;
124
125
0
  pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
126
0
  obuf = wmem_array_sized_new(pool, 1, in_size*2);
127
128
0
  TRY {
129
0
                ok = do_uncompress(tvb, offset, in_size, obuf);
130
0
  } CATCH_ALL {
131
0
    ok = false;
132
0
  }
133
0
  ENDTRY;
134
135
0
  if (ok) {
136
    /*
137
     * Cannot pass a tvb free callback that frees the wmem
138
     * pool, so we make an extra copy that uses bare
139
     * pointers. This could be optimized if tvb API had a
140
     * free pool callback of some sort.
141
     */
142
0
    unsigned size = wmem_array_get_count(obuf);
143
0
    uint8_t *p = (uint8_t *)g_malloc(size);
144
0
    memcpy(p, wmem_array_get_raw(obuf), size);
145
0
    out = tvb_new_real_data(p, size, size);
146
0
    tvb_set_free_cb(out, g_free);
147
0
  } else {
148
0
    out = NULL;
149
0
  }
150
151
0
  wmem_destroy_allocator(pool);
152
153
0
  return out;
154
0
}
155
156
tvbuff_t *
157
tvb_child_uncompress_lznt1(tvbuff_t *parent, tvbuff_t *tvb, const unsigned offset, unsigned in_size)
158
0
{
159
0
  tvbuff_t *new_tvb = tvb_uncompress_lznt1(tvb, offset, in_size);
160
0
  if (new_tvb)
161
0
    tvb_set_child_real_data_tvbuff(parent, new_tvb);
162
0
  return new_tvb;
163
0
}
164
165
/*
166
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
167
 *
168
 * Local variables:
169
 * c-basic-offset: 8
170
 * tab-width: 8
171
 * indent-tabs-mode: t
172
 * End:
173
 *
174
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
175
 * :indentSize=8:tabSize=8:noTabs=false:
176
 */