/* tvbuff_composite.c * * Copyright (c) 2000 by Gilbert Ramirez * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "tvbuff.h" #include "tvbuff-int.h" #include "proto.h" /* XXX - only used for DISSECTOR_ASSERT, probably a new header file? */ typedef struct { GSList *tvbs; /* Used for quick testing to see if this * is the tvbuff that a COMPOSITE is * interested in. */ guint *start_offsets; guint *end_offsets; } tvb_comp_t; struct tvb_composite { struct tvbuff tvb; tvb_comp_t composite; }; static void composite_free(tvbuff_t *tvb) { struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb; tvb_comp_t *composite = &composite_tvb->composite; g_slist_free(composite->tvbs); g_free(composite->start_offsets); g_free(composite->end_offsets); g_free((gpointer)tvb->real_data); } static guint composite_offset(const tvbuff_t *tvb _U_, const guint counter) { return counter; } static const guint8* composite_get_ptr(tvbuff_t *tvb, guint abs_offset, guint abs_length) { struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb; guint i, num_members; tvb_comp_t *composite; tvbuff_t *member_tvb = NULL; guint member_offset; GSList *slist; /* DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); */ /* Maybe the range specified by offset/length * is contiguous inside one of the member tvbuffs */ composite = &composite_tvb->composite; num_members = g_slist_length(composite->tvbs); for (i = 0; i < num_members; i++) { if (abs_offset <= composite->end_offsets[i]) { slist = g_slist_nth(composite->tvbs, i); member_tvb = (tvbuff_t *)slist->data; break; } } /* special case */ if (!member_tvb) { DISSECTOR_ASSERT(abs_offset == tvb->length && abs_length == 0); return ""; } member_offset = abs_offset - composite->start_offsets[i]; if (tvb_bytes_exist(member_tvb, member_offset, abs_length)) { /* * The range is, in fact, contiguous within member_tvb. */ DISSECTOR_ASSERT(!tvb->real_data); return tvb_get_ptr(member_tvb, member_offset, abs_length); } else { /* Use a temporary variable as tvb_memcpy is also checking tvb->real_data pointer */ void *real_data = g_malloc(tvb->length); tvb_memcpy(tvb, real_data, 0, tvb->length); tvb->real_data = (const guint8 *)real_data; return tvb->real_data + abs_offset; } DISSECTOR_ASSERT_NOT_REACHED(); } static void * composite_memcpy(tvbuff_t *tvb, void* _target, guint abs_offset, guint abs_length) { struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb; guint8 *target = (guint8 *) _target; guint i, num_members; tvb_comp_t *composite; tvbuff_t *member_tvb = NULL; guint member_offset, member_length; GSList *slist; /* DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); */ /* Maybe the range specified by offset/length * is contiguous inside one of the member tvbuffs */ composite = &composite_tvb->composite; num_members = g_slist_length(composite->tvbs); for (i = 0; i < num_members; i++) { if (abs_offset <= composite->end_offsets[i]) { slist = g_slist_nth(composite->tvbs, i); member_tvb = (tvbuff_t *)slist->data; break; } } /* special case */ if (!member_tvb) { DISSECTOR_ASSERT(abs_offset == tvb->length && abs_length == 0); return target; } member_offset = abs_offset - composite->start_offsets[i]; if (tvb_bytes_exist(member_tvb, member_offset, abs_length)) { DISSECTOR_ASSERT(!tvb->real_data); return tvb_memcpy(member_tvb, target, member_offset, abs_length); } else { /* The requested data is non-contiguous inside * the member tvb. We have to memcpy() the part that's in the member tvb, * then iterate across the other member tvb's, copying their portions * until we have copied all data. */ member_length = tvb_captured_length_remaining(member_tvb, member_offset); /* composite_memcpy() can't handle a member_length of zero. */ DISSECTOR_ASSERT(member_length > 0); tvb_memcpy(member_tvb, target, member_offset, member_length); abs_offset += member_length; abs_length -= member_length; /* Recurse */ if (abs_length > 0) { composite_memcpy(tvb, target + member_length, abs_offset, abs_length); } return target; } DISSECTOR_ASSERT_NOT_REACHED(); } static const struct tvb_ops tvb_composite_ops = { sizeof(struct tvb_composite), /* size */ composite_free, /* free */ composite_offset, /* offset */ composite_get_ptr, /* get_ptr */ composite_memcpy, /* memcpy */ NULL, /* find_guint8 XXX */ NULL, /* pbrk_guint8 XXX */ NULL, /* clone */ }; /* * Composite tvb * * A composite TVB references the concatenation of one or more TVBs, each of * them MUST be part of the same chain (the same memory "scope"). The * caller of tvb_new_composite MUST immediately call tvb_composite_append or * tvb_composite_prepend to ensure that the composite TVB is properly freed as * needed. * * Failure to satisfy the same chain requirement can result in memory-safety * issues such as use-after-free or double-free. */ tvbuff_t * tvb_new_composite(void) { tvbuff_t *tvb = tvb_new(&tvb_composite_ops); struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb; tvb_comp_t *composite = &composite_tvb->composite; composite->tvbs = NULL; composite->start_offsets = NULL; composite->end_offsets = NULL; return tvb; } void tvb_composite_append(tvbuff_t *tvb, tvbuff_t *member) { struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb; tvb_comp_t *composite; DISSECTOR_ASSERT(tvb && !tvb->initialized); DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); /* Don't allow zero-length TVBs: composite_memcpy() can't handle them * and anyway it makes no sense. */ if (member && member->length) { composite = &composite_tvb->composite; composite->tvbs = g_slist_append(composite->tvbs, member); /* Attach the composite TVB to the first TVB only. */ if (!composite->tvbs->next) { tvb_add_to_chain((tvbuff_t *)composite->tvbs->data, tvb); } } } void tvb_composite_prepend(tvbuff_t *tvb, tvbuff_t *member) { struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb; tvb_comp_t *composite; DISSECTOR_ASSERT(tvb && !tvb->initialized); DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); /* Don't allow zero-length TVBs: composite_memcpy() can't handle them * and anyway it makes no sense. */ if (member && member->length) { composite = &composite_tvb->composite; composite->tvbs = g_slist_prepend(composite->tvbs, member); /* Attach the composite TVB to the first TVB only. */ if (!composite->tvbs->next) { tvb_add_to_chain((tvbuff_t *)composite->tvbs->data, tvb); } } } void tvb_composite_finalize(tvbuff_t *tvb) { struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb; GSList *slist; guint num_members; tvbuff_t *member_tvb; tvb_comp_t *composite; int i = 0; DISSECTOR_ASSERT(tvb && !tvb->initialized); DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); DISSECTOR_ASSERT(tvb->length == 0); DISSECTOR_ASSERT(tvb->reported_length == 0); DISSECTOR_ASSERT(tvb->contained_length == 0); composite = &composite_tvb->composite; num_members = g_slist_length(composite->tvbs); /* Dissectors should not create composite TVBs if they're not going to * put at least one TVB in them. * (Without this check--or something similar--we'll seg-fault below.) */ DISSECTOR_ASSERT(num_members); composite->start_offsets = g_new(guint, num_members); composite->end_offsets = g_new(guint, num_members); for (slist = composite->tvbs; slist != NULL; slist = slist->next) { DISSECTOR_ASSERT((guint) i < num_members); member_tvb = (tvbuff_t *)slist->data; composite->start_offsets[i] = tvb->length; tvb->length += member_tvb->length; tvb->reported_length += member_tvb->reported_length; tvb->contained_length += member_tvb->contained_length; composite->end_offsets[i] = tvb->length - 1; i++; } tvb->initialized = TRUE; tvb->ds_tvb = tvb; } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */