wireshark/epan/tvbuff_composite.c

309 lines
8.3 KiB
C

/* tvbuff_composite.c
*
* Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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:
*/