Add sanity checks to make sure the claimed block size is big enough to:
authorguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 22 Feb 2012 18:32:43 +0000 (18:32 +0000)
committerguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 22 Feb 2012 18:32:43 +0000 (18:32 +0000)
1) contain the block length fields and block type field;

2) contain that plus the fixed-length portion of the block;

3) for blocks that have a variable-length portion other than the
   options, contain that variable-length portion.

Fixes a crash we're seeing with a bad pcap-NG file in the Wireshark
menagerie (7799-lastPacketWithoutComment.pcapng - the last packet's
block length is 128, but it claims to have 98 bytes of packet data,
which requires a 132-byte block).

Clean up white space (use 8-space tabs).

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@41143 f5534014-38df-0310-8fa8-9805f1628bb7

wiretap/pcapng.c

index 3627508..f7f17f0 100644 (file)
@@ -101,6 +101,11 @@ typedef struct pcapng_block_header_s {
        /* guint32 block_total_length */
 } pcapng_block_header_t;
 
+/*
+ * Minimum block size = size of block header + size of block trailer.
+ */
+#define MIN_BLOCK_SIZE ((guint32)(sizeof(pcapng_block_header_t) + sizeof(guint32)))
+
 /* pcapng: section header block */
 typedef struct pcapng_section_header_block_s {
        /* pcapng_block_header_t */
@@ -111,6 +116,11 @@ typedef struct pcapng_section_header_block_s {
        /* ... Options ... */
 } pcapng_section_header_block_t;
 
+/*
+ * Minimum SHB size = minimum block size + size of fixed length portion of SHB.
+ */
+#define MIN_SHB_SIZE   ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_section_header_block_t)))
+
 /* pcapng: interface description block */
 typedef struct pcapng_interface_description_block_s {
        guint16 linktype;
@@ -119,6 +129,11 @@ typedef struct pcapng_interface_description_block_s {
        /* ... Options ... */
 } pcapng_interface_description_block_t;
 
+/*
+ * Minimum IDB size = minimum block size + size of fixed length portion of IDB.
+ */
+#define MIN_IDB_SIZE   ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_interface_description_block_t)))
+
 /* pcapng: packet block (obsolete) */
 typedef struct pcapng_packet_block_s {
        guint16 interface_id;
@@ -132,6 +147,11 @@ typedef struct pcapng_packet_block_s {
        /* ... Options ... */
 } pcapng_packet_block_t;
 
+/*
+ * Minimum PB size = minimum block size + size of fixed length portion of PB.
+ */
+#define MIN_PB_SIZE    ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_packet_block_t)))
+
 /* pcapng: enhanced packet block */
 typedef struct pcapng_enhanced_packet_block_s {
        guint32 interface_id;
@@ -144,6 +164,11 @@ typedef struct pcapng_enhanced_packet_block_s {
        /* ... Options ... */
 } pcapng_enhanced_packet_block_t;
 
+/*
+ * Minimum EPB size = minimum block size + size of fixed length portion of EPB.
+ */
+#define MIN_EPB_SIZE   ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_enhanced_packet_block_t)))
+
 /* pcapng: simple packet block */
 typedef struct pcapng_simple_packet_block_s {
        guint32 packet_len;
@@ -151,6 +176,11 @@ typedef struct pcapng_simple_packet_block_s {
        /* ... Padding ... */
 } pcapng_simple_packet_block_t;
 
+/*
+ * Minimum SPB size = minimum block size + size of fixed length portion of SPB.
+ */
+#define MIN_SPB_SIZE   ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_simple_packet_block_t)))
+
 /* pcapng: name resolution block */
 typedef struct pcapng_name_resolution_block_s {
        guint16 record_type;
@@ -158,6 +188,12 @@ typedef struct pcapng_name_resolution_block_s {
        /* ... Record ... */
 } pcapng_name_resolution_block_t;
 
+/*
+ * Minimum NRB size = minimum block size + size of smallest NRB record
+ * (there must at least be an "end of records" record).
+ */
+#define MIN_NRB_SIZE   ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_name_resolution_block_t)))
+
 /* pcapng: interface statistics block */
 typedef struct pcapng_interface_statistics_block_s {
        guint32 interface_id;
@@ -166,6 +202,11 @@ typedef struct pcapng_interface_statistics_block_s {
        /* ... Options ... */
 } pcapng_interface_statistics_block_t;
 
+/*
+ * Minimum ISB size = minimum block size + size of fixed length portion of ISB.
+ */
+#define MIN_ISB_SIZE   ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_interface_statistics_block_t)))
+
 /* pcapng: common option header for every option type */
 typedef struct pcapng_option_header_s {
        guint16 option_code;
@@ -445,6 +486,20 @@ pcapng_read_section_header_block(FILE_T fh, gboolean first_block,
        pcapng_option_header_t oh;
        char *option_content = NULL; /* Allocate as large as the options block */
 
+       /*
+        * Is this block long enough to be an SHB?
+        */
+       if (bh->block_total_length < MIN_SHB_SIZE) {
+               /*
+                * No.
+                */
+               if (first_block)
+                       return 0;       /* probably not a pcap-ng file */
+               *err = WTAP_ERR_BAD_FILE;
+               *err_info = g_strdup_printf("pcapng_read_section_header_block: total block length %u of an SHB is less than the minimum SHB size %u",
+                             bh->block_total_length, MIN_SHB_SIZE);
+               return -1;
+       }
 
        /* read block content */
        errno = WTAP_ERR_CANT_READ;
@@ -539,10 +594,7 @@ pcapng_read_section_header_block(FILE_T fh, gboolean first_block,
 
        /* Options */
        errno = WTAP_ERR_CANT_READ;
-       to_read = bh->block_total_length -
-                 (int)sizeof(pcapng_block_header_t) -
-                 (int)sizeof(pcapng_section_header_block_t) -
-                 (int)sizeof(bh->block_total_length);
+       to_read = bh->block_total_length - MIN_SHB_SIZE;
        /* Allocate enough memory to hold all options */
        opt_cont_buf_len = to_read;
        option_content = g_malloc(opt_cont_buf_len);
@@ -623,7 +675,18 @@ pcapng_read_if_descr_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn,
        pcapng_option_header_t oh;
        char *option_content = NULL; /* Allocate as large as the options block */
 
-
+       /*
+        * Is this block long enough to be an IDB?
+        */
+       if (bh->block_total_length < MIN_IDB_SIZE) {
+               /*
+                * No.
+                */
+               *err = WTAP_ERR_BAD_FILE;
+               *err_info = g_strdup_printf("pcapng_read_if_descr_block: total block length %u of an IDB is less than the minimum IDB size %u",
+                             bh->block_total_length, MIN_IDB_SIZE);
+               return -1;
+       }
 
        /* read block content */
        errno = WTAP_ERR_CANT_READ;
@@ -682,10 +745,7 @@ pcapng_read_if_descr_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn,
 
        /* Options */
        errno = WTAP_ERR_CANT_READ;
-       to_read = bh->block_total_length -
-                 (int)sizeof(pcapng_block_header_t) -
-                 (int)sizeof (pcapng_interface_description_block_t) -
-                 (int)sizeof(bh->block_total_length);
+       to_read = bh->block_total_length - MIN_IDB_SIZE;
 
        /* Allocate enough memory to hold all options */
        opt_cont_buf_len = to_read;
@@ -855,6 +915,7 @@ pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wta
        pcapng_enhanced_packet_block_t epb;
        pcapng_packet_block_t pb;
        guint32 block_total_length;
+       guint32 padding;
        pcapng_option_header_t oh;
        gint wtap_encap;
        int pseudo_header_len;
@@ -864,6 +925,18 @@ pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wta
        /* "(Enhanced) Packet Block" read fixed part */
        errno = WTAP_ERR_CANT_READ;
        if (enhanced) {
+               /*
+                * Is this block long enough to be an EPB?
+                */
+               if (bh->block_total_length < MIN_EPB_SIZE) {
+                       /*
+                        * No.
+                        */
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("pcapng_read_packet_block: total block length %u of an EPB is less than the minimum EPB size %u",
+                                     bh->block_total_length, MIN_EPB_SIZE);
+                       return -1;
+               }
                bytes_read = file_read(&epb, sizeof epb, fh);
                if (bytes_read != sizeof epb) {
                        pcapng_debug0("pcapng_read_packet_block: failed to read packet data");
@@ -875,19 +948,31 @@ pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wta
                if (pn->byte_swapped) {
                        wblock->data.packet.interface_id        = BSWAP32(epb.interface_id);
                        wblock->data.packet.drops_count         = -1; /* invalid */
-                       wblock->data.packet.ts_high                     = BSWAP32(epb.timestamp_high);
-                       wblock->data.packet.ts_low                      = BSWAP32(epb.timestamp_low);
-                       wblock->data.packet.cap_len                     = BSWAP32(epb.captured_len);
+                       wblock->data.packet.ts_high             = BSWAP32(epb.timestamp_high);
+                       wblock->data.packet.ts_low              = BSWAP32(epb.timestamp_low);
+                       wblock->data.packet.cap_len             = BSWAP32(epb.captured_len);
                        wblock->data.packet.packet_len          = BSWAP32(epb.packet_len);
                } else {
                        wblock->data.packet.interface_id        = epb.interface_id;
                        wblock->data.packet.drops_count         = -1; /* invalid */
-                       wblock->data.packet.ts_high                     = epb.timestamp_high;
-                       wblock->data.packet.ts_low                      = epb.timestamp_low;
-                       wblock->data.packet.cap_len                     = epb.captured_len;
+                       wblock->data.packet.ts_high             = epb.timestamp_high;
+                       wblock->data.packet.ts_low              = epb.timestamp_low;
+                       wblock->data.packet.cap_len             = epb.captured_len;
                        wblock->data.packet.packet_len          = epb.packet_len;
                }
        } else {
+               /*
+                * Is this block long enough to be a PB?
+                */
+               if (bh->block_total_length < MIN_PB_SIZE) {
+                       /*
+                        * No.
+                        */
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("pcapng_read_packet_block: total block length %u of a PB is less than the minimum PB size %u",
+                                     bh->block_total_length, MIN_PB_SIZE);
+                       return -1;
+               }
                bytes_read = file_read(&pb, sizeof pb, fh);
                if (bytes_read != sizeof pb) {
                        pcapng_debug0("pcapng_read_packet_block: failed to read packet data");
@@ -899,18 +984,62 @@ pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wta
                if (pn->byte_swapped) {
                        wblock->data.packet.interface_id        = BSWAP16(pb.interface_id);
                        wblock->data.packet.drops_count         = BSWAP16(pb.drops_count);
-                       wblock->data.packet.ts_high                     = BSWAP32(pb.timestamp_high);
-                       wblock->data.packet.ts_low                      = BSWAP32(pb.timestamp_low);
-                       wblock->data.packet.cap_len                     = BSWAP32(pb.captured_len);
+                       wblock->data.packet.ts_high             = BSWAP32(pb.timestamp_high);
+                       wblock->data.packet.ts_low              = BSWAP32(pb.timestamp_low);
+                       wblock->data.packet.cap_len             = BSWAP32(pb.captured_len);
                        wblock->data.packet.packet_len          = BSWAP32(pb.packet_len);
                } else {
                        wblock->data.packet.interface_id        = pb.interface_id;
                        wblock->data.packet.drops_count         = pb.drops_count;
-                       wblock->data.packet.ts_high                     = pb.timestamp_high;
-                       wblock->data.packet.ts_low                      = pb.timestamp_low;
-                       wblock->data.packet.cap_len                     = pb.captured_len;
+                       wblock->data.packet.ts_high             = pb.timestamp_high;
+                       wblock->data.packet.ts_low              = pb.timestamp_low;
+                       wblock->data.packet.cap_len             = pb.captured_len;
                        wblock->data.packet.packet_len          = pb.packet_len;
                }
+
+       }
+
+       /*
+        * How much padding is there at the end of the packet data?
+        */
+       if ((wblock->data.packet.cap_len % 4) != 0)
+               padding = 4 - (wblock->data.packet.cap_len % 4);
+       else
+               padding = 0;
+
+       /* add padding bytes to "block total length" */
+       /* (the "block total length" of some example files don't contain the packet data padding bytes!) */
+       if (bh->block_total_length % 4) {
+               block_total_length = bh->block_total_length + 4 - (bh->block_total_length % 4);
+       } else {
+               block_total_length = bh->block_total_length;
+       }
+
+       /*
+        * Is this block long enough to hold the packet data?
+        */
+       if (enhanced) {
+               if (block_total_length <
+                   MIN_EPB_SIZE + wblock->data.packet.cap_len + padding) {
+                       /*
+                        * No.
+                        */
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("pcapng_read_packet_block: total block length %u of EPB is too small for %u bytes of packet data",
+                                     block_total_length, wblock->data.packet.cap_len);
+                       return -1;
+               }
+       } else {
+               if (block_total_length <
+                   MIN_PB_SIZE + wblock->data.packet.cap_len + padding) {
+                       /*
+                        * No.
+                        */
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("pcapng_read_packet_block: total block length %u of PB is too small for %u bytes of packet data",
+                                     block_total_length, wblock->data.packet.cap_len);
+                       return -1;
+               }
        }
 
        if (wblock->data.packet.cap_len > wblock->data.packet.packet_len) {
@@ -970,22 +1099,14 @@ pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn, wta
        block_read += bytes_read;
 
        /* jump over potential padding bytes at end of the packet data */
-       if( (wblock->data.packet.cap_len % 4) != 0) {
-               file_offset64 = file_seek(fh, 4 - (wblock->data.packet.cap_len % 4), SEEK_CUR, err);
+       if (padding != 0) {
+               file_offset64 = file_seek(fh, padding, SEEK_CUR, err);
                if (file_offset64 <= 0) {
                        if (*err != 0)
                                return -1;
                        return 0;
                }
-               block_read += 4 - (wblock->data.packet.cap_len % 4);
-       }
-
-       /* add padding bytes to "block total length" */
-       /* (the "block total length" of some example files don't contain the packet data padding bytes!) */
-       if (bh->block_total_length % 4) {
-               block_total_length = bh->block_total_length + 4 - (bh->block_total_length % 4);
-       } else {
-               block_total_length = bh->block_total_length;
+               block_read += padding;
        }
 
        /* Option defaults */
@@ -1101,6 +1222,18 @@ pcapng_read_simple_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *
        int pseudo_header_len;
        pcapng_simple_packet_block_t spb;
 
+       /*
+        * Is this block long enough to be an SPB?
+        */
+       if (bh->block_total_length < MIN_SPB_SIZE) {
+               /*
+                * No.
+                */
+               *err = WTAP_ERR_BAD_FILE;
+               *err_info = g_strdup_printf("pcapng_read_simple_packet_block: total block length %u of an SPB is less than the minimum SPB size %u",
+                             bh->block_total_length, MIN_SPB_SIZE);
+               return -1;
+       }
 
        /* "Simple Packet Block" read fixed part */
        errno = WTAP_ERR_CANT_READ;
@@ -1118,7 +1251,7 @@ pcapng_read_simple_packet_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *
                wblock->data.simple_packet.packet_len   = spb.packet_len;
        }
 
-       wblock->data.simple_packet.cap_len = bh->block_total_length
+       wblock->data.simple_packet.cap_len = bh->block_total_length -
                                             - (guint32)sizeof(pcapng_simple_packet_block_t)
                                             - (guint32)sizeof(bh->block_total_length);
 
@@ -1206,12 +1339,34 @@ pcapng_read_name_resolution_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t
        guint8 nrb_rec[MAX_NRB_REC_SIZE];
        guint32 v4_addr;
 
+       /*
+        * Is this block long enough to be an NRB?
+        */
+       if (bh->block_total_length < MIN_NRB_SIZE) {
+               /*
+                * No.
+                */
+               *err = WTAP_ERR_BAD_FILE;
+               *err_info = g_strdup_printf("pcapng_read_name_resolution_block: total block length %u is too small (< %u)",
+                             bh->block_total_length, MIN_NRB_SIZE);
+               return -1;
+       }
+
        errno = WTAP_ERR_CANT_READ;
-       to_read = bh->block_total_length
-               - sizeof(pcapng_block_header_t)
-               - sizeof(bh->block_total_length);
+       to_read = bh->block_total_length - MIN_NRB_SIZE;
 
        while (block_read < to_read) {
+               /*
+                * There must be at least one record's worth of data
+                * here.
+                */
+               if ((size_t)(to_read - block_read) < sizeof nrb) {
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("pcapng_read_name_resolution_block: %d bytes left in the block < NRB record header size %u",
+                                     to_read - block_read,
+                                     (guint)sizeof nrb);
+                       return -1;
+               }
                bytes_read = file_read(&nrb, sizeof nrb, fh);
                if (bytes_read != sizeof nrb) {
                        pcapng_debug0("pcapng_read_name_resolution_block: failed to read record header");
@@ -1225,13 +1380,26 @@ pcapng_read_name_resolution_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t
                        nrb.record_len  = BSWAP16(nrb.record_len);
                }
 
+               if (to_read - block_read < nrb.record_len + PADDING4(nrb.record_len)) {
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("pcapng_read_name_resolution_block: %d bytes left in the block < NRB record length + padding %u",
+                                     to_read - block_read,
+                                     nrb.record_len + PADDING4(nrb.record_len));
+                       return -1;
+               }
                switch(nrb.record_type) {
                        case NRES_ENDOFRECORD:
                                /* There shouldn't be any more data */
                                to_read = 0;
                                break;
                        case NRES_IP4RECORD:
-                               if (nrb.record_len < 6 || nrb.record_len > MAX_NRB_REC_SIZE || to_read < nrb.record_len) {
+                               if (nrb.record_len < 4) {
+                                       *err = WTAP_ERR_BAD_FILE;
+                                       *err_info = g_strdup_printf("pcapng_read_name_resolution_block: NRB record length for IPv4 record %u < minimum length 4",
+                                                     nrb.record_len);
+                                       return -1;
+                               }
+                               if (nrb.record_len > MAX_NRB_REC_SIZE) {
                                        pcapng_debug0("pcapng_read_name_resolution_block: bad length or insufficient data for IPv4 record");
                                        return 0;
                                }
@@ -1259,7 +1427,13 @@ pcapng_read_name_resolution_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t
                                block_read += PADDING4(nrb.record_len);
                                break;
                        case NRES_IP6RECORD:
-                               if (nrb.record_len < 18 || nrb.record_len > MAX_NRB_REC_SIZE || to_read < nrb.record_len) {
+                               if (nrb.record_len < 16) {
+                                       *err = WTAP_ERR_BAD_FILE;
+                                       *err_info = g_strdup_printf("pcapng_read_name_resolution_block: NRB record length for IPv6 record %u < minimum length 16",
+                                                     nrb.record_len);
+                                       return -1;
+                               }
+                               if (nrb.record_len > MAX_NRB_REC_SIZE || to_read < nrb.record_len) {
                                        pcapng_debug0("pcapng_read_name_resolution_block: bad length or insufficient data for IPv6 record");
                                        return 0;
                                }
@@ -1309,6 +1483,18 @@ pcapng_read_interface_statistics_block(FILE_T fh, pcapng_block_header_t *bh, pca
        pcapng_option_header_t oh;
        char *option_content = NULL; /* Allocate as large as the options block */
 
+       /*
+        * Is this block long enough to be an ISB?
+        */
+       if (bh->block_total_length < MIN_ISB_SIZE) {
+               /*
+                * No.
+                */
+               *err = WTAP_ERR_BAD_FILE;
+               *err_info = g_strdup_printf("pcapng_read_name_resolution_block: total block length %u is too small (< %u)",
+                             bh->block_total_length, MIN_NRB_SIZE);
+               return -1;
+       }
 
        /* "Interface Statistics Block" read fixed part */
        errno = WTAP_ERR_CANT_READ;
@@ -1339,9 +1525,7 @@ pcapng_read_interface_statistics_block(FILE_T fh, pcapng_block_header_t *bh, pca
        /* Options */
        errno = WTAP_ERR_CANT_READ;
        to_read = bh->block_total_length -
-                 sizeof(pcapng_block_header_t) -
-                 block_read -    /* fixed and variable part, including padding */
-                 sizeof(bh->block_total_length);
+                 (MIN_BLOCK_SIZE + block_read);    /* fixed and variable part, including padding */
 
        /* Allocate enough memory to hold all options */
        opt_cont_buf_len = to_read;
@@ -1473,7 +1657,7 @@ pcapng_read_interface_statistics_block(FILE_T fh, pcapng_block_header_t *bh, pca
 
        g_free(option_content);
 
-    return block_read;
+       return block_read;
 }
 
 
@@ -1493,7 +1677,7 @@ pcapng_read_unknown_block(FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn _U_
                block_total_length = bh->block_total_length;
        }
 
-       block_read = block_total_length - (guint32)sizeof(pcapng_block_header_t) - (guint32)sizeof(bh->block_total_length);
+       block_read = block_total_length - MIN_BLOCK_SIZE;
 
        /* jump over this unknown block */
        file_offset64 = file_seek(fh, block_read, SEEK_CUR, err);
@@ -1551,6 +1735,19 @@ pcapng_read_block(FILE_T fh, gboolean first_block, pcapng_t *pn, wtapng_block_t
                        return 0;       /* not a pcap-ng file */
        }
 
+       if (bh.block_total_length < MIN_BLOCK_SIZE) {
+               /*
+                * This isn't even enough for the block type and 2
+                * block total length fields.
+                */
+               if (first_block)
+                       return 0;       /* probably not a pcap-ng file */
+               *err = WTAP_ERR_BAD_FILE;
+               *err_info = g_strdup_printf("pcapng_read_block: total block length %u is too small (< %u)",
+                             bh.block_total_length, (guint32)MIN_BLOCK_SIZE);
+               return -1;
+       }
+
        switch(bh.block_type) {
                case(BLOCK_TYPE_SHB):
                        bytes_read = pcapng_read_section_header_block(fh, first_block, &bh, pn, wblock, err, err_info);