WBXML: add a basic sanity check for offset overflow

This is a naive approach allowing to detact that something went wrong,
without the need to replace all proto_tree_add_text() calls as what was
done in master-2.0 branch.

Bug: 12408
Change-Id: Ia14905005e17ae322c2fc639ad5e491fa08b0108
Reviewed-on: https://code.wireshark.org/review/15310
Reviewed-by: Michael Mann <mmann78@netscape.net>
Reviewed-by: Pascal Quantin <pascal.quantin@gmail.com>
dissect_wbxml_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
		     const wbxml_decoding *override_content_map)
{
	/* Set up structures needed to add the protocol subtree and manage it */
	proto_item           *ti;
	proto_tree           *wbxml_tree;          /* Main WBXML tree */
	proto_tree           *wbxml_str_tbl_tree;  /* String table subtree */
	proto_tree           *wbxml_content_tree;  /* Content subtree */
	guint8                version;
	guint                 offset          = 0;
	guint32               len;
	guint32               charset         = 0;
	guint32               charset_len     = 0;
	guint32               publicid;
	guint32               publicid_index  = 0;
	guint32               publicid_len;
	guint32               str_tbl;
	guint32               str_tbl_len;
	guint32               str_tbl_len_len = 0;
	guint8                level           = 0; /* WBXML recursion level */
	const wbxml_decoding *content_map     = NULL;
	gchar                *summary         = NULL;
	guint8                codepage_stag   = 0;
	guint8                codepage_attr   = 0;

	DebugLog(("dissect_wbxml: Dissecting packet %u\n", pinfo->fd->num));
	/* WBXML format
	 *
	 * Version 1.0: version publicid         strtbl BODY
	 * Version 1.x: version publicid charset strtbl BODY
	 *
	 * Last valid format: WBXML 1.3
	 */
	switch ( version = tvb_get_guint8 (tvb, 0) ) {
	case 0x00: /* WBXML/1.0 */
		break;

	case 0x01: /* WBXML/1.1 */
	case 0x02: /* WBXML/1.2 */
	case 0x03: /* WBXML/1.3 */
		break;

	default:
		/* Put some information here, so that the user knows what's going on. */

		/* Add summary to INFO column if it is enabled */
		col_append_fstr(pinfo->cinfo, COL_INFO, " (Unknown WBXML version 0x%02x)", version);
		ti = proto_tree_add_item (tree, proto_wbxml, tvb, 0, -1, ENC_NA);
		proto_item_append_text(ti, ", Unknown version 0x%02x", version);
		return;
	}

	/* In order to properly construct the packet summary,
	 * I need to read the entire WBXML header
	 * up to the string table length.
	 */

	/* Public ID */
	publicid = tvb_get_guintvar(tvb, 1, &publicid_len);
	if (! publicid) {
		/* Public identifier in string table */
		publicid_index = tvb_get_guintvar (tvb, 1+publicid_len, &len);
		publicid_len += len;
	}
	offset = 1 + publicid_len;

	/* Version-specific handling of Charset */
	switch ( version ) {
	case 0x00: /* WBXML/1.0 */
		/* No charset */
		break;

	case 0x01: /* WBXML/1.1 */
	case 0x02: /* WBXML/1.2 */
	case 0x03: /* WBXML/1.3 */
		/* Get charset */
		charset = tvb_get_guintvar (tvb, offset, &charset_len);
		offset += charset_len;
		break;

	default: /* Impossible since we returned already earlier */
		DISSECTOR_ASSERT_NOT_REACHED();
		break;
	}

	/* String table: read string table length in bytes */
	tvb_get_guintvar (tvb, offset, &str_tbl_len_len);
	str_tbl = offset + str_tbl_len_len; /* Start of 1st string in string table */

	/* Compose the summary line */
	if ( publicid ) {
		summary = wmem_strdup_printf(wmem_packet_scope(), "%s, Public ID: \"%s\"",
					  val_to_str_ext (version, &vals_wbxml_versions_ext, "(unknown 0x%x)"),
					  val_to_str_ext (publicid, &vals_wbxml_public_ids_ext, "(unknown 0x%x)"));
	} else {
		/* Read length of Public ID from string table */
		len = tvb_strsize (tvb, str_tbl + publicid_index);
		summary = wmem_strdup_printf(wmem_packet_scope(), "%s, Public ID: \"%s\"",
					  val_to_str_ext (version, &vals_wbxml_versions_ext, "(unknown 0x%x)"),
					  tvb_format_text (tvb, str_tbl + publicid_index, len - 1));
	}

	/* Add summary to INFO column if it is enabled */
	col_append_fstr(pinfo->cinfo, COL_INFO, " (WBXML %s)", summary);

	/* create display subtree for the protocol */
	ti = proto_tree_add_item (tree, proto_wbxml, tvb, 0, -1, ENC_NA);
	proto_item_append_text(ti, ", Version: %s", summary);

	/*
	 * Now show the protocol subtree, if tree is set.
	 */
	if ( tree ) {
		wbxml_tree = proto_item_add_subtree(ti, ett_wbxml);

		/* WBXML Version */
		proto_tree_add_uint (wbxml_tree, hf_wbxml_version,
				     tvb, 0, 1, version);

		/* Public ID */
		if (publicid) { /* Known Public ID */
			proto_tree_add_uint(wbxml_tree, hf_wbxml_public_id_known,
					    tvb, 1, publicid_len, publicid);
		} else { /* Public identifier in string table */
			proto_tree_add_item (wbxml_tree, hf_wbxml_public_id_literal,
					     tvb, 1, publicid_len, ENC_ASCII|ENC_NA);
		}
		offset = 1 + publicid_len;

		if ( version ) { /* Charset */
			proto_tree_add_uint (wbxml_tree, hf_wbxml_charset,
					     tvb, 1 + publicid_len, charset_len, charset);
			offset += charset_len;
		}

		str_tbl_len = tvb_get_guintvar (tvb, offset, &len);
		str_tbl = offset + len; /* Start of 1st string in string table */

		/* String Table */
		ti = proto_tree_add_text(wbxml_tree,
					 tvb, offset, len + str_tbl_len, "String table: %u bytes",
					 str_tbl_len);

		if (wbxml_tree && str_tbl_len) { /* Display string table as subtree */
			wbxml_str_tbl_tree = proto_item_add_subtree (ti,
								     ett_wbxml_str_tbl);
			show_wbxml_string_table (wbxml_str_tbl_tree, tvb,
						 str_tbl, str_tbl_len);
		}

		/* Data starts HERE */
		offset += len + str_tbl_len;

		/* The WBXML BODY starts here */
		if (disable_wbxml_token_parsing) {
			proto_tree_add_text (wbxml_tree, tvb, offset, -1,
						  "Data representation not shown "
						  "(edit WBXML preferences to show)");
			return;
		} /* Else: render the WBXML tokens */
		ti = proto_tree_add_text (wbxml_tree, tvb, offset, -1,
					  "Data representation");
		wbxml_content_tree = proto_item_add_subtree (ti, ett_wbxml_content);

		/* The parse_wbxml_X() functions will process the content correctly,
		 * irrespective of the WBXML version used. For the WBXML body, this
		 * means that there is a different processing for the global token
		 * RESERVED_2 (WBXML 1.0) or OPAQUE (WBXML 1.x with x > 0).  */
		if (wbxml_tree) { /* Show only if visible */
			if (override_content_map != NULL) {
				content_map = override_content_map;
				proto_item_append_text(ti,
						       " is based on: %s",
						       content_map->name);
			} else {
				/* Retrieve the content token mapping if available */
				content_map = get_wbxml_decoding_from_public_id (publicid);
				if (! content_map) {
					content_map = get_wbxml_decoding_from_content_type(
											   pinfo->match_string, tvb, offset);
					if (! content_map) {
						proto_tree_add_text (wbxml_content_tree,
								     tvb, offset, -1,
								     "[Rendering of this content type"
								     " not (yet) supported]");
					} else {
						proto_item_append_text(ti,
								       " is based on Content-Type: %s "
								       "(chosen decoding: %s)",
								       pinfo->match_string, content_map->name);
					}
				}
			}
			if (content_map && skip_wbxml_token_mapping) {
				proto_tree_add_text (wbxml_content_tree,
						     tvb, offset, -1,
						     "[Rendering of this content type"
						     " has been disabled "
						     "(edit WBXML preferences to enable)]");
				content_map = NULL;
			}
			proto_tree_add_text (wbxml_content_tree, tvb,
					     offset, -1,
					     "Level | State | Codepage "
					     "| WBXML Token Description         "
					     "| Rendering");
			if (content_map) {
				len = parse_wbxml_tag_defined (wbxml_content_tree,
							       tvb, offset, str_tbl, &level, &codepage_stag,
							       &codepage_attr, content_map);
			} else {
				/* Default: WBXML only, no interpretation of the content */
				len = parse_wbxml_tag (wbxml_content_tree, tvb, offset,
						       str_tbl, &level, &codepage_stag, &codepage_attr);
			}
		}
		return;
	}
}

dissect_wbxml_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
		     const wbxml_decoding *override_content_map)
{
	/* Set up structures needed to add the protocol subtree and manage it */
	proto_item           *ti;
	proto_tree           *wbxml_tree;          /* Main WBXML tree */
	proto_tree           *wbxml_str_tbl_tree;  /* String table subtree */
	proto_tree           *wbxml_content_tree;  /* Content subtree */
	guint8                version;
	guint                 offset          = 0;
	guint32               len;
	guint32               charset         = 0;
	guint32               charset_len     = 0;
	guint32               publicid;
	guint32               publicid_index  = 0;
	guint32               publicid_len;
	guint32               str_tbl;
	guint32               str_tbl_len;
	guint32               str_tbl_len_len = 0;
	guint8                level           = 0; /* WBXML recursion level */
	const wbxml_decoding *content_map     = NULL;
	gchar                *summary         = NULL;
	guint8                codepage_stag   = 0;
	guint8                codepage_attr   = 0;

	DebugLog(("dissect_wbxml: Dissecting packet %u\n", pinfo->fd->num));
	/* WBXML format
	 *
	 * Version 1.0: version publicid         strtbl BODY
	 * Version 1.x: version publicid charset strtbl BODY
	 *
	 * Last valid format: WBXML 1.3
	 */
	switch ( version = tvb_get_guint8 (tvb, 0) ) {
	case 0x00: /* WBXML/1.0 */
		break;

	case 0x01: /* WBXML/1.1 */
	case 0x02: /* WBXML/1.2 */
	case 0x03: /* WBXML/1.3 */
		break;

	default:
		/* Put some information here, so that the user knows what's going on. */

		/* Add summary to INFO column if it is enabled */
		col_append_fstr(pinfo->cinfo, COL_INFO, " (Unknown WBXML version 0x%02x)", version);
		ti = proto_tree_add_item (tree, proto_wbxml, tvb, 0, -1, ENC_NA);
		proto_item_append_text(ti, ", Unknown version 0x%02x", version);
		return;
	}

	/* In order to properly construct the packet summary,
	 * I need to read the entire WBXML header
	 * up to the string table length.
	 */

	/* Public ID */
	publicid = tvb_get_guintvar(tvb, 1, &publicid_len);
	if (! publicid) {
		/* Public identifier in string table */
		publicid_index = tvb_get_guintvar (tvb, 1+publicid_len, &len);
		publicid_len += len;
	}
	offset = 1 + publicid_len;

	/* Version-specific handling of Charset */
	switch ( version ) {
	case 0x00: /* WBXML/1.0 */
		/* No charset */
		break;

	case 0x01: /* WBXML/1.1 */
	case 0x02: /* WBXML/1.2 */
	case 0x03: /* WBXML/1.3 */
		/* Get charset */
		charset = tvb_get_guintvar (tvb, offset, &charset_len);
		offset += charset_len;
		break;

	default: /* Impossible since we returned already earlier */
		DISSECTOR_ASSERT_NOT_REACHED();
		break;
	}

	/* String table: read string table length in bytes */
	tvb_get_guintvar (tvb, offset, &str_tbl_len_len);
	str_tbl = offset + str_tbl_len_len; /* Start of 1st string in string table */

	/* Compose the summary line */
	if ( publicid ) {
		summary = wmem_strdup_printf(wmem_packet_scope(), "%s, Public ID: \"%s\"",
					  val_to_str_ext (version, &vals_wbxml_versions_ext, "(unknown 0x%x)"),
					  val_to_str_ext (publicid, &vals_wbxml_public_ids_ext, "(unknown 0x%x)"));
	} else {
		/* Read length of Public ID from string table */
		len = tvb_strsize (tvb, str_tbl + publicid_index);
		summary = wmem_strdup_printf(wmem_packet_scope(), "%s, Public ID: \"%s\"",
					  val_to_str_ext (version, &vals_wbxml_versions_ext, "(unknown 0x%x)"),
					  tvb_format_text (tvb, str_tbl + publicid_index, len - 1));
	}

	/* Add summary to INFO column if it is enabled */
	col_append_fstr(pinfo->cinfo, COL_INFO, " (WBXML %s)", summary);

	/* create display subtree for the protocol */
	ti = proto_tree_add_item (tree, proto_wbxml, tvb, 0, -1, ENC_NA);
	proto_item_append_text(ti, ", Version: %s", summary);

	/*
	 * Now show the protocol subtree, if tree is set.
	 */
	if ( tree ) {
		wbxml_tree = proto_item_add_subtree(ti, ett_wbxml);

		/* WBXML Version */
		proto_tree_add_uint (wbxml_tree, hf_wbxml_version,
				     tvb, 0, 1, version);

		/* Public ID */
		if (publicid) { /* Known Public ID */
			proto_tree_add_uint(wbxml_tree, hf_wbxml_public_id_known,
					    tvb, 1, publicid_len, publicid);
		} else { /* Public identifier in string table */
			proto_tree_add_item (wbxml_tree, hf_wbxml_public_id_literal,
					     tvb, 1, publicid_len, ENC_ASCII|ENC_NA);
		}
		offset = 1 + publicid_len;

		if ( version ) { /* Charset */
			proto_tree_add_uint (wbxml_tree, hf_wbxml_charset,
					     tvb, 1 + publicid_len, charset_len, charset);
			offset += charset_len;
		}

		str_tbl_len = tvb_get_guintvar (tvb, offset, &len);
		str_tbl = offset + len; /* Start of 1st string in string table */

		/* String Table */
		ti = proto_tree_add_text(wbxml_tree,
					 tvb, offset, len + str_tbl_len, "String table: %u bytes",
					 str_tbl_len);

		if (wbxml_tree && str_tbl_len) { /* Display string table as subtree */
			wbxml_str_tbl_tree = proto_item_add_subtree (ti,
								     ett_wbxml_str_tbl);
			show_wbxml_string_table (wbxml_str_tbl_tree, tvb,
						 str_tbl, str_tbl_len);
		}

		/* Data starts HERE */
		offset += len + str_tbl_len;

		/* The WBXML BODY starts here */
		if (disable_wbxml_token_parsing) {
			proto_tree_add_text (wbxml_tree, tvb, offset, -1,
						  "Data representation not shown "
						  "(edit WBXML preferences to show)");
			return;
		} /* Else: render the WBXML tokens */
		ti = proto_tree_add_text (wbxml_tree, tvb, offset, -1,
					  "Data representation");
		wbxml_content_tree = proto_item_add_subtree (ti, ett_wbxml_content);

		/* The parse_wbxml_X() functions will process the content correctly,
		 * irrespective of the WBXML version used. For the WBXML body, this
		 * means that there is a different processing for the global token
		 * RESERVED_2 (WBXML 1.0) or OPAQUE (WBXML 1.x with x > 0).  */
		if (wbxml_tree) { /* Show only if visible */
			if (override_content_map != NULL) {
				content_map = override_content_map;
				proto_item_append_text(ti,
						       " is based on: %s",
						       content_map->name);
			} else {
				/* Retrieve the content token mapping if available */
				content_map = get_wbxml_decoding_from_public_id (publicid);
				if (! content_map) {
					content_map = get_wbxml_decoding_from_content_type(
											   pinfo->match_string, tvb, offset);
					if (! content_map) {
						proto_tree_add_text (wbxml_content_tree,
								     tvb, offset, -1,
								     "[Rendering of this content type"
								     " not (yet) supported]");
					} else {
						proto_item_append_text(ti,
								       " is based on Content-Type: %s "
								       "(chosen decoding: %s)",
								       pinfo->match_string, content_map->name);
					}
				}
			}
			if (content_map && skip_wbxml_token_mapping) {
				proto_tree_add_text (wbxml_content_tree,
						     tvb, offset, -1,
						     "[Rendering of this content type"
						     " has been disabled "
						     "(edit WBXML preferences to enable)]");
				content_map = NULL;
			}
			proto_tree_add_text (wbxml_content_tree, tvb,
					     offset, -1,
					     "Level | State | Codepage "
					     "| WBXML Token Description         "
					     "| Rendering");
			if (content_map) {
				len = parse_wbxml_tag_defined (wbxml_content_tree,
							       tvb, offset, str_tbl, &level, &codepage_stag,
							       &codepage_attr, content_map);
			} else {
				/* Default: WBXML only, no interpretation of the content */
				len = parse_wbxml_tag (wbxml_content_tree, tvb, offset,
						       str_tbl, &level, &codepage_stag, &codepage_attr);
			}
		}
		return;
	}
}
