]> The Tcpdump Group git mirrors - libpcap/commitdiff
Allow bigger D-Bus packets, but don't allocate big buffers initially.
authorGuy Harris <[email protected]>
Thu, 1 Jun 2017 20:44:32 +0000 (13:44 -0700)
committerGuy Harris <[email protected]>
Thu, 1 Jun 2017 20:44:32 +0000 (13:44 -0700)
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.

pcap-common.c
pcap-common.h
sf-pcap-ng.c
sf-pcap.c

index f97737f619f9bd3b7603faa7d24d703141acf0e1..7083e3f44204893e71aa31ea231af335ff3102c4 100644 (file)
@@ -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
index 6ac5bcd264f9cfb6e02fdd09b916e77254fb7726..b8d1f314e8e0d8c83eb28b16a0e890581bc085af 100644 (file)
@@ -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,
@@ -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);
index a6ab985acac72803d0e86516a0b8d0b2a0d5c218..b81f9875de2319073b418a314a7e732f4c21c9af 100644 (file)
@@ -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,
@@ -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 */
@@ -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);
@@ -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)
@@ -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
@@ -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;
 
index 2210d5ff2c64dbed55650e82b4b2e6519b3ef697..67929c0f35a621dab4dfd4b97577f2c12b13bacd 100644 (file)
--- a/sf-pcap.c
+++ b/sf-pcap.c
@@ -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);
 
@@ -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");
@@ -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
@@ -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);
@@ -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) {