Add sanity checks to make sure the claimed block size is big enough to:

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).

svn path=/trunk/; revision=41143
This commit is contained in:
Guy Harris 2012-02-22 18:32:43 +00:00
parent 0ebef9a0fa
commit 3b262a0621
1 changed files with 240 additions and 43 deletions

View 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);