Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/tvbuff_composite.c
Line
Count
Source (jump to first uncovered line)
1
/* tvbuff_composite.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
14
#include "tvbuff.h"
15
#include "tvbuff-int.h"
16
#include "proto.h"  /* XXX - only used for DISSECTOR_ASSERT, probably a new header file? */
17
18
typedef struct {
19
  GQueue    *tvbs;
20
21
  /* Used for quick testing to see if this
22
   * is the tvbuff that a COMPOSITE is
23
   * interested in. */
24
  unsigned    *start_offsets;
25
  unsigned    *end_offsets;
26
27
  unsigned  recursion_depth;
28
29
} tvb_comp_t;
30
31
struct tvb_composite {
32
  struct tvbuff tvb;
33
34
  tvb_comp_t  composite;
35
};
36
37
static void
38
composite_free(tvbuff_t *tvb)
39
44.6k
{
40
44.6k
  struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
41
44.6k
  tvb_comp_t *composite = &composite_tvb->composite;
42
43
44.6k
  g_queue_free(composite->tvbs);
44
45
44.6k
  g_free(composite->start_offsets);
46
44.6k
  g_free(composite->end_offsets);
47
44.6k
  g_free((void *)tvb->real_data);
48
44.6k
}
49
50
static unsigned
51
composite_offset(const tvbuff_t *tvb _U_, const unsigned counter)
52
41.9k
{
53
41.9k
  return counter;
54
41.9k
}
55
56
static const uint8_t*
57
composite_get_ptr(tvbuff_t *tvb, unsigned abs_offset, unsigned abs_length)
58
2.98M
{
59
2.98M
  struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
60
2.98M
  unsigned      i;
61
2.98M
  tvb_comp_t *composite;
62
2.98M
  tvbuff_t   *member_tvb = NULL;
63
2.98M
  unsigned  member_offset;
64
65
  /* DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); */
66
67
  /* Maybe the range specified by offset/length
68
   * is contiguous inside one of the member tvbuffs */
69
2.98M
  composite = &composite_tvb->composite;
70
2.98M
  GList *item = (GList*)composite->tvbs->head;
71
72
73
5.55M
  for (i = 0; i < g_queue_get_length(composite->tvbs); i++, item=item->next) {
74
5.55M
    if (abs_offset <= composite->end_offsets[i]) {
75
2.98M
      member_tvb = (tvbuff_t *)item->data;
76
2.98M
      break;
77
2.98M
    }
78
5.55M
  }
79
80
  /* special case */
81
2.98M
  if (!member_tvb) {
82
1
    DISSECTOR_ASSERT(abs_offset == tvb->length && abs_length == 0);
83
1
    return "";
84
1
  }
85
86
2.98M
  member_offset = abs_offset - composite->start_offsets[i];
87
88
2.98M
  if (tvb_bytes_exist(member_tvb, member_offset, abs_length)) {
89
    /*
90
     * The range is, in fact, contiguous within member_tvb.
91
     */
92
2.98M
    DISSECTOR_ASSERT(!tvb->real_data);
93
2.98M
    return tvb_get_ptr(member_tvb, member_offset, abs_length);
94
2.98M
  }
95
725
  else {
96
    /* Use a temporary variable as tvb_memcpy is also checking tvb->real_data pointer */
97
725
    void *real_data = g_malloc(tvb->length);
98
725
    tvb_memcpy(tvb, real_data, 0, tvb->length);
99
725
    tvb->real_data = (const uint8_t *)real_data;
100
725
    return tvb->real_data + abs_offset;
101
725
  }
102
103
0
  DISSECTOR_ASSERT_NOT_REACHED();
104
0
}
105
106
#define MAX_RECURSION_DEPTH 500 // Arbitrary; matches prefs.gui_max_tree_depth
107
static void *
108
// NOLINTNEXTLINE(misc-no-recursion)
109
composite_memcpy(tvbuff_t *tvb, void* _target, unsigned abs_offset, unsigned abs_length)
110
12.6k
{
111
12.6k
  struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
112
12.6k
  uint8_t *target = (uint8_t *) _target;
113
114
12.6k
  unsigned      i;
115
12.6k
  tvb_comp_t *composite;
116
12.6k
  tvbuff_t   *member_tvb = NULL;
117
12.6k
  unsigned      member_offset, member_length;
118
119
  /* DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); */
120
121
  /* Maybe the range specified by offset/length
122
   * is contiguous inside one of the member tvbuffs */
123
12.6k
  composite   = &composite_tvb->composite;
124
125
12.6k
  GList *item = (GList*)composite->tvbs->head;
126
163k
  for (i = 0; i < g_queue_get_length(composite->tvbs); i++, item=item->next) {
127
163k
    if (abs_offset <= composite->end_offsets[i]) {
128
12.6k
      member_tvb = (tvbuff_t *)item->data;
129
12.6k
      break;
130
12.6k
    }
131
163k
  }
132
133
  /* special case */
134
12.6k
  if (!member_tvb) {
135
0
    DISSECTOR_ASSERT(abs_offset == tvb->length && abs_length == 0);
136
0
    return target;
137
0
  }
138
139
12.6k
  member_offset = abs_offset - composite->start_offsets[i];
140
141
12.6k
  if (tvb_bytes_exist(member_tvb, member_offset, abs_length)) {
142
9.30k
    DISSECTOR_ASSERT(!tvb->real_data);
143
9.30k
    return tvb_memcpy(member_tvb, target, member_offset, abs_length);
144
9.30k
  }
145
3.34k
  else {
146
    /* The requested data is non-contiguous inside
147
     * the member tvb. We have to memcpy() the part that's in the member tvb,
148
     * then iterate across the other member tvb's, copying their portions
149
     * until we have copied all data.
150
     */
151
3.34k
    member_length = tvb_captured_length_remaining(member_tvb, member_offset);
152
153
    /* composite_memcpy() can't handle a member_length of zero. */
154
3.34k
    DISSECTOR_ASSERT(member_length > 0);
155
    /* make sure we don't underflow below */
156
3.34k
    DISSECTOR_ASSERT(member_length <= abs_length);
157
158
3.34k
    tvb_memcpy(member_tvb, target, member_offset, member_length);
159
3.34k
    abs_offset  += member_length;
160
3.34k
    abs_length  -= member_length;
161
162
    /* Recurse */
163
3.34k
    if (abs_length > 0) {
164
3.34k
      composite->recursion_depth++;
165
3.34k
      DISSECTOR_ASSERT(composite->recursion_depth < MAX_RECURSION_DEPTH);
166
3.34k
      composite_memcpy(tvb, target + member_length, abs_offset, abs_length);
167
3.34k
      composite->recursion_depth--;
168
3.34k
    }
169
170
3.34k
    return target;
171
3.34k
  }
172
173
0
  DISSECTOR_ASSERT_NOT_REACHED();
174
0
}
175
176
static const struct tvb_ops tvb_composite_ops = {
177
  sizeof(struct tvb_composite), /* size */
178
179
  composite_free,       /* free */
180
  composite_offset,     /* offset */
181
  composite_get_ptr,    /* get_ptr */
182
  composite_memcpy,     /* memcpy */
183
  NULL,                 /* find_uint8 XXX */
184
  NULL,                 /* pbrk_uint8 XXX */
185
  NULL,                 /* clone */
186
};
187
188
/*
189
 * Composite tvb
190
 *
191
 * A composite TVB references the concatenation of one or more TVBs, each of
192
 * them MUST be part of the same chain (the same memory "scope"). The
193
 * caller of tvb_new_composite MUST immediately call tvb_composite_append or
194
 * tvb_composite_prepend to ensure that the composite TVB is properly freed as
195
 * needed.
196
 *
197
 * Failure to satisfy the same chain requirement can result in memory-safety
198
 * issues such as use-after-free or double-free.
199
 */
200
tvbuff_t *
201
tvb_new_composite(void)
202
44.6k
{
203
44.6k
  tvbuff_t *tvb = tvb_new(&tvb_composite_ops);
204
44.6k
  struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
205
44.6k
  tvb_comp_t *composite = &composite_tvb->composite;
206
207
44.6k
  composite->tvbs    = g_queue_new();
208
44.6k
  composite->start_offsets = NULL;
209
44.6k
  composite->end_offsets   = NULL;
210
44.6k
  composite->recursion_depth = 0;
211
212
44.6k
  return tvb;
213
44.6k
}
214
215
void
216
tvb_composite_append(tvbuff_t *tvb, tvbuff_t *member)
217
97.2k
{
218
97.2k
  struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
219
97.2k
  tvb_comp_t *composite;
220
221
97.2k
  DISSECTOR_ASSERT(tvb && !tvb->initialized);
222
97.2k
  DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops);
223
224
  /* Don't allow zero-length TVBs: composite_memcpy() can't handle them
225
   * and anyway it makes no sense.
226
   */
227
97.2k
  if (member && member->length) {
228
97.2k
    composite       = &composite_tvb->composite;
229
97.2k
    g_queue_push_tail(composite->tvbs, member);
230
231
    /* Attach the composite TVB to the first TVB only. */
232
97.2k
    if (g_queue_get_length(composite->tvbs) == 1) {
233
44.6k
      tvb_add_to_chain((tvbuff_t *)g_queue_peek_head(composite->tvbs), tvb);
234
44.6k
    }
235
97.2k
  }
236
97.2k
}
237
238
void
239
tvb_composite_prepend(tvbuff_t *tvb, tvbuff_t *member)
240
0
{
241
0
  struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
242
0
  tvb_comp_t *composite;
243
244
0
  DISSECTOR_ASSERT(tvb && !tvb->initialized);
245
0
  DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops);
246
247
  /* Don't allow zero-length TVBs: composite_memcpy() can't handle them
248
   * and anyway it makes no sense.
249
   */
250
0
  if (member && member->length) {
251
0
    composite       = &composite_tvb->composite;
252
0
    g_queue_push_head(composite->tvbs, member);
253
254
    /* Attach the composite TVB to the first TVB only. */
255
0
    if (g_queue_get_length(composite->tvbs) == 1) {
256
0
      tvb_add_to_chain((tvbuff_t *)g_queue_peek_head(composite->tvbs), tvb);
257
0
    }
258
0
  }
259
0
}
260
261
void
262
tvb_composite_finalize(tvbuff_t *tvb)
263
44.4k
{
264
44.4k
  struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
265
266
44.4k
  unsigned  num_members;
267
44.4k
  tvbuff_t   *member_tvb;
268
44.4k
  tvb_comp_t *composite;
269
44.4k
  unsigned  i;
270
271
44.4k
  DISSECTOR_ASSERT(tvb && !tvb->initialized);
272
44.4k
  DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops);
273
44.4k
  DISSECTOR_ASSERT(tvb->length == 0);
274
44.4k
  DISSECTOR_ASSERT(tvb->reported_length == 0);
275
44.4k
  DISSECTOR_ASSERT(tvb->contained_length == 0);
276
277
44.4k
  composite   = &composite_tvb->composite;
278
279
44.4k
  num_members = g_queue_get_length(composite->tvbs);
280
281
  /* Dissectors should not create composite TVBs if they're not going to
282
   * put at least one TVB in them.
283
   * (Without this check--or something similar--we'll seg-fault below.)
284
   */
285
44.4k
  DISSECTOR_ASSERT(num_members);
286
287
44.4k
  composite->start_offsets = g_new(unsigned, num_members);
288
44.4k
  composite->end_offsets = g_new(unsigned, num_members);
289
290
44.4k
  GList *item = (GList*)composite->tvbs->head;
291
141k
  for (i=0; i < num_members; i++, item=item->next) {
292
96.9k
    member_tvb = (tvbuff_t *)item->data;
293
96.9k
    composite->start_offsets[i] = tvb->length;
294
96.9k
    tvb->length += member_tvb->length;
295
96.9k
    tvb->reported_length += member_tvb->reported_length;
296
96.9k
    tvb->contained_length += member_tvb->contained_length;
297
96.9k
    composite->end_offsets[i] = tvb->length - 1;
298
96.9k
  }
299
300
44.4k
  tvb->initialized = true;
301
44.4k
  tvb->ds_tvb = tvb;
302
44.4k
}
303
304
/*
305
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
306
 *
307
 * Local variables:
308
 * c-basic-offset: 8
309
 * tab-width: 8
310
 * indent-tabs-mode: t
311
 * End:
312
 *
313
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
314
 * :indentSize=8:tabSize=8:noTabs=false:
315
 */