/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 | | */ |