Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow bigger D-Bus packets, but don't allocate big buffers initially.
D-Bus packets can be up to 128MB in size, but don't allocate 128MB for
every pcap_t, or even every D-Bus pcap_t.  Start out with enough to
handle 2KB packets - that should be enough for most regular network
packets - and grow the buffer as necessary to handle packets up to the
snapshot length.

Treat packets bigger than the maximum snapshot length for the link-layer
type as errors; we continue to use MAXIMUM_SNAPLEN (256KB) for most
link-layer header types, but use 128MB for DLT_DBUS, as per the above.

If the snapshot length in the file header or IDB is 0, or is > 2^31-1,
use the maximum snapshot length for the link-layer header type as the
snapshot length.  The check for 2^31-1 is there because the current API
for snapshot lengths returns an int, and thus can't handle snapshot
lengths > 2^31-1, even though the field is actually unsigned.
  • Loading branch information
guyharris committed Jun 1, 2017
1 parent 28f1b20 commit 1a6b088
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 47 deletions.
17 changes: 16 additions & 1 deletion pcap-common.c
Expand Up @@ -1196,7 +1196,22 @@ linktype_to_dlt(int linktype)
return linktype;
}

#define EXTRACT_
/*
* Return the maximum snapshot length for a given DLT_ value.
*
* For most link-layer types, we use MAXIMUM_SNAPLEN, but for DLT_DBUS,
* the maximum is 134217728, as per
*
* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages
*/
u_int
max_snaplen_for_dlt(int dlt)
{
if (dlt == DLT_DBUS)
return 134217728;
else
return MAXIMUM_SNAPLEN;
}

/*
* DLT_LINUX_SLL packets with a protocol type of LINUX_SLL_P_CAN or
Expand Down
24 changes: 24 additions & 0 deletions pcap-common.h
@@ -1,3 +1,25 @@
/*
* Copyright (c) 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* pcap-common.h - common code for pcap and pcap-ng files
*/

/*
* We use the "receiver-makes-right" approach to byte order,
Expand All @@ -23,3 +45,5 @@ extern int linktype_to_dlt(int linktype);

extern void swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr,
u_char *data);

extern u_int max_snaplen_for_dlt(int dlt);
75 changes: 57 additions & 18 deletions sf-pcap-ng.c
Expand Up @@ -215,32 +215,44 @@ struct pcap_ng_if {
u_int64_t tsoffset; /* time stamp offset */
};

/*
* Per-pcap_t private data.
*
* max_blocksize is the maximum size of a block that we'll accept. We
* reject blocks bigger than this, so we don't consume too much memory
* with a truly huge block. It can change as we see IDBs with different
* link-layer header types. (Currently, we don't support IDBs with
* different link-layer header types, but we will support it in the
* future, when we offer file-reading APIs that support it.)
*
* XXX - that's an issue on ILP32 platforms, where the maximum block
* size of 2^31-1 would eat all but one byte of the entire address space.
* It's less of an issue on ILP64/LLP64 platforms, but the actual size
* of the address space may be limited by 1) the number of *significant*
* address bits (currently, x86-64 only supports 48 bits of address), 2)
* any limitations imposed by the operating system; 3) any limitations
* imposed by the amount of available backing store for anonymous pages,
* so we impose a limit regardless of the size of a pointer.
*/
struct pcap_ng_sf {
u_int user_tsresol; /* time stamp resolution requested by the user */
u_int max_blocksize; /* don't grow buffer size past this */
bpf_u_int32 ifcount; /* number of interfaces seen in this capture */
bpf_u_int32 ifaces_size; /* size of array below */
struct pcap_ng_if *ifaces; /* array of interface information */
};

/*
* Maximum block size; we reject blocks bigger than this, so we don't
* consume too much memory with a truly huge block.
* Maximum block size for a given maximum snapshot length; we calculate
* this based
*
* We define it as the size of an EPB with a MAXIMUM_SNAPLEN-sized
* We define it as the size of an EPB with a max_snaplen-sized
* packet and 128KB of options.
*
* XXX - that's an issue on ILP32 platforms, where the maximum block
* size of 2^31-1 would eat all but one byte of the entire address space.
* It's less of an issue on ILP64/LLP64 platforms, but the actual size
* of the address space may be limited by 1) the number of *significant*
* address bits (currently, x86-64 only supports 48 bits of address), 2)
* any limitations imposed by the operating system; 3) any limitations
* imposed by the amount of available backing store for anonymous pages.
*/
#define MAX_BLOCKSIZE (sizeof (struct block_header) + \
sizeof (struct enhanced_packet_block) + \
MAXIMUM_SNAPLEN + 131072 + \
sizeof (struct block_trailer))
#define MAX_BLOCKSIZE(max_snaplen) (sizeof (struct block_header) + \
sizeof (struct enhanced_packet_block) + \
(max_snaplen) + 131072 + \
sizeof (struct block_trailer))

static void pcap_ng_cleanup(pcap_t *p);
static int pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr,
Expand Down Expand Up @@ -274,11 +286,14 @@ read_bytes(FILE *fp, void *buf, size_t bytes_to_read, int fail_on_eof,
static int
read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf)
{
struct pcap_ng_sf *ps;
int status;
struct block_header bhdr;
u_char *bdata;
size_t data_remaining;

ps = p->priv;

status = read_bytes(fp, &bhdr, sizeof(bhdr), 0, errbuf);
if (status <= 0)
return (status); /* error or EOF */
Expand Down Expand Up @@ -324,9 +339,9 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf)
*/
void *bigger_buffer;

if (bhdr.total_length > MAX_BLOCKSIZE) {
if (bhdr.total_length > ps->max_blocksize) {
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "block is larger than maximum block size %u",
(u_int) MAX_BLOCKSIZE);
ps->max_blocksize);
return (-1);
}
bigger_buffer = realloc(p->buffer, bhdr.total_length);
Expand Down Expand Up @@ -873,7 +888,11 @@ pcap_ng_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
* Packet Block containing a full-size Ethernet frame, and
* leaving room for some options.
*
* If we find a bigger block, we reallocate the buffer.
* If we find a bigger block, we reallocate the buffer, up to
* the maximum size. We start out with a maximum size based
* on a maximum snapshot length of MAXIMUM_SNAPLEN; if we see
* any link-layer header types with a larger maximum snapshot
* length, we boost the maximum.
*/
p->bufsize = 2048;
if (p->bufsize < total_length)
Expand All @@ -885,6 +904,7 @@ pcap_ng_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
*err = 1;
return (NULL);
}
ps->max_blocksize = MAX_BLOCKSIZE(MAXIMUM_SNAPLEN);

/*
* Copy the stuff we've read to the buffer, and read the rest
Expand Down Expand Up @@ -995,9 +1015,28 @@ pcap_ng_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
done:
p->tzoff = 0; /* XXX - not used in pcap */
p->snapshot = idbp->snaplen;
if (p->snapshot <= 0) {
/*
* Bogus snapshot length; use the maximum for this
* link-layer type as a fallback.
*
* XXX - the only reason why snapshot is signed is
* that pcap_snapshot() returns an int, not an
* unsigned int.
*/
p->snapshot = max_snaplen_for_dlt(idbp->linktype);
}
p->linktype = linktype_to_dlt(idbp->linktype);
p->linktype_ext = 0;

/*
* If the maximum block size for a packet with the maximum
* snapshot length for this DLT_ is bigger than the current
* maximum block size, increase the maximum.
*/
if (MAX_BLOCKSIZE(max_snaplen_for_dlt(p->linktype)) > ps->max_blocksize)
ps->max_blocksize = MAX_BLOCKSIZE(max_snaplen_for_dlt(p->linktype));

p->next_packet_op = pcap_ng_next_packet;
p->cleanup_op = pcap_ng_cleanup;

Expand Down
145 changes: 117 additions & 28 deletions sf-pcap.c
Expand Up @@ -242,6 +242,17 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
p->version_minor = hdr.version_minor;
p->tzoff = hdr.thiszone;
p->snapshot = hdr.snaplen;
if (p->snapshot <= 0) {
/*
* Bogus snapshot length; use the maximum for this
* link-layer type as a fallback.
*
* XXX - the only reason why snapshot is signed is
* that pcap_snapshot() returns an int, not an
* unsigned int.
*/
p->snapshot = max_snaplen_for_dlt(hdr.linktype);
}
p->linktype = linktype_to_dlt(LT_LINKTYPE(hdr.linktype));
p->linktype_ext = LT_LINKTYPE_EXT(hdr.linktype);

Expand Down Expand Up @@ -377,20 +388,16 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,

/*
* Allocate a buffer for the packet data.
* Don't allocate more than MAXIMUM_SNAPLEN.
* Choose the minimum of the file's snapshot length and 2K bytes;
* that should be enough for most network packets - we'll grow it
* if necessary. That way, we don't allocate a huge chunk of
* memory just because there's a huge snapshot length, as the
* snapshot length might be larger than the size of the largest
* packet.
*/
p->bufsize = p->snapshot;
if (p->bufsize > MAXIMUM_SNAPLEN) {
/*
* Too-large snapshot length; trim it at the maximum.
*/
p->bufsize = MAXIMUM_SNAPLEN;
} else if (p->bufsize <= 0) {
/*
* Bogus snapshot length; use the maximum as a fallback.
*/
p->bufsize = MAXIMUM_SNAPLEN;
}
if (p->bufsize > 2048)
p->bufsize = 2048;
p->buffer = malloc(p->bufsize);
if (p->buffer == NULL) {
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory");
Expand All @@ -404,6 +411,24 @@ pcap_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf,
return (p);
}

/*
* Grow the packet buffer to the specified size.
*/
static int
grow_buffer(pcap_t *p, u_int bufsize)
{
void *bigger_buffer;

bigger_buffer = realloc(p->buffer, bufsize);
if (bigger_buffer == NULL) {
pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "out of memory");
return (0);
}
p->buffer = bigger_buffer;
p->bufsize = bufsize;
return (1);
}

/*
* Read and return the next packet from the savefile. Return the header
* in hdr and a pointer to the contents in data. Return 0 on success, 1
Expand Down Expand Up @@ -506,33 +531,71 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
break;
}

if (hdr->caplen > p->bufsize) {
/*
* Is the packet bigger than we consider sane?
*/
if (hdr->caplen > max_snaplen_for_dlt(p->linktype)) {
/*
* Yes. This may be a damaged or fuzzed file.
*
* Is it bigger than the snapshot length?
* (We don't treat that as an error if it's not
* bigger than the maximum we consider sane; see
* below.)
*/
if (hdr->caplen > (bpf_u_int32)p->snapshot) {
pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"invalid packet capture length %u, bigger than "
"snaplen of %d", hdr->caplen, p->snapshot);
} else {
pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"invalid packet capture length %u, bigger than "
"maximum of %u", hdr->caplen,
max_snaplen_for_dlt(p->linktype));
}
return (-1);
}

if (hdr->caplen > (bpf_u_int32)p->snapshot) {
/*
* The packet is bigger than the snapshot length
* for this file.
*
* This can happen due to Solaris 2.3 systems tripping
* over the BUFMOD problem and not setting the snapshot
* correctly in the savefile header.
* This can also happen with a corrupted savefile or a
* savefile built/modified by a fuzz tester.
* If the caplen isn't grossly wrong, try to salvage.
* length correctly in the savefile header.
*
* libpcap 0.4 and later on Solaris 2.3 should set the
* snapshot length correctly in the pcap file header,
* even though they don't set a snapshot length in bufmod
* (the buggy bufmod chops off the *beginning* of the
* packet if a snapshot length is specified); they should
* also reduce the captured length, as supplied to the
* per-packet callback, to the snapshot length if it's
* greater than the snapshot length, so the code using
* libpcap should see the packet cut off at the snapshot
* length, even though the full packet is copied up to
* userland.
*
* However, perhaps some versions of libpcap failed to
* set the snapshot length currectly in the file header
* or the per-packet header, or perhaps this is a
* corrupted safefile or a savefile built/modified by a
* fuzz tester, so we check anyway.
*/
size_t bytes_to_discard;
size_t bytes_to_read, bytes_read;
char discard_buf[4096];

if (hdr->caplen > MAXIMUM_SNAPLEN) {
pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"invalid packet capture length %u, bigger than "
"maximum of %u", hdr->caplen, MAXIMUM_SNAPLEN);
return (-1);
if (hdr->caplen > p->bufsize) {
/*
* Grow the buffer to the snapshot length.
*/
if (!grow_buffer(p, p->snapshot))
return (-1);
}

/*
* XXX - we don't grow the buffer here because some
* program might assume that it will never get packets
* bigger than the snapshot length; for example, it might
* copy data from our buffer to a buffer of its own,
* allocated based on the return value of pcap_snapshot().
*
* Read the first p->bufsize bytes into the buffer.
*/
amt_read = fread(p->buffer, 1, p->bufsize, fp);
Expand Down Expand Up @@ -588,6 +651,32 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
*/
hdr->caplen = p->bufsize;
} else {
if (hdr->caplen > p->bufsize) {
/*
* Grow the buffer to the next power of 2, or
* the snaplen, whichever is lower.
*/
u_int new_bufsize;

new_bufsize = hdr->caplen;
/*
* http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
*/
new_bufsize--;
new_bufsize |= new_bufsize >> 1;
new_bufsize |= new_bufsize >> 2;
new_bufsize |= new_bufsize >> 4;
new_bufsize |= new_bufsize >> 8;
new_bufsize |= new_bufsize >> 16;
new_bufsize++;

if (new_bufsize > (u_int)p->snapshot)
new_bufsize = p->snapshot;

if (!grow_buffer(p, new_bufsize))
return (-1);
}

/* read the packet itself */
amt_read = fread(p->buffer, 1, hdr->caplen, fp);
if (amt_read != hdr->caplen) {
Expand Down

0 comments on commit 1a6b088

Please sign in to comment.