nsh: fix stack overflow due to missing length checks

After v2.3.0rc0-3167-ge6f944d632, the NSH dissector could call itself
recursively and since the minimum header length was not validated, it
could result in a stack overflow due to infinite recursion.

Add checks based on the text from
https://tools.ietf.org/html/draft-ietf-sfc-nsh-12#section-3.2

This patch also fixes a regression since v2.3.0rc0-3171-g2273cf0e7b
where the wrong tvb was passed to subdissectors. Tested with the two
captures from bug 11490.

Bug: 13612
Link: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1156
Change-Id: I8cacfa267557e8373ff8134f4b020d927e37842f
Reviewed-on: https://code.wireshark.org/review/21499
Reviewed-by: Michael Mann <mmann78@netscape.net>
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2017-05-04 20:03:35 +02:00 committed by Anders Broman
parent a9be088128
commit ce8863c6ef
1 changed files with 80 additions and 51 deletions

View File

@ -29,6 +29,7 @@
#include "config.h"
#include <epan/packet.h>
#include <epan/etypes.h>
#include <epan/expert.h>
#include "packet-nsh.h"
#include "packet-vxlan.h"
@ -68,6 +69,8 @@ static int hf_nsh_metadata_reservedbits = -1;
static int hf_nsh_metadata_length = -1;
static int hf_nsh_metadata = -1;
static expert_field ei_nsh_length_invalid = EI_INIT;
static gint ett_nsh = -1;
static dissector_table_t subdissector_table;
@ -133,75 +136,92 @@ dissect_nsh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
int offset = 0;
int md_type = -1;
int nsh_bytes_len = 0;
guint32 nsh_bytes_len;
int nsh_next_proto = -1;
int captured_length;
proto_item *length_pi;
tvbuff_t *next_tvb;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "NSH");
col_set_str(pinfo->cinfo, COL_INFO, "Network Service Header");
captured_length = tvb_captured_length(tvb);
proto_item *ti;
proto_tree *nsh_tree;
ti = proto_tree_add_item(tree, proto_nsh, tvb, offset, 2, ENC_NA);
nsh_tree = proto_item_add_subtree(ti, ett_nsh);
/*NSH Base Header*/
proto_tree_add_item(nsh_tree, hf_nsh_version, tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(nsh_tree, hf_nsh_oam, tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(nsh_tree, hf_nsh_critical_metadata, tvb, offset, 2, ENC_BIG_ENDIAN);
if (tree) {
proto_item *ti;
proto_tree *nsh_tree;
/* Bits 10 - 15 contain length value */
nsh_bytes_len = 4 * tvb_get_bits8(tvb, 10, 6);
ti = proto_tree_add_item(tree, proto_nsh, tvb, offset, nsh_bytes_len, ENC_NA);
nsh_tree = proto_item_add_subtree(ti, ett_nsh);
/*NSH Base Header*/
proto_tree_add_item(nsh_tree, hf_nsh_version, tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(nsh_tree, hf_nsh_oam, tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(nsh_tree, hf_nsh_critical_metadata, tvb, offset, 2, ENC_BIG_ENDIAN);
/* Bits 4 - 9 are reserved */
proto_tree_add_item(nsh_tree, hf_nsh_reservedbits, tvb, offset, 2, ENC_BIG_ENDIAN);
length_pi = proto_tree_add_item_ret_uint(nsh_tree, hf_nsh_length, tvb, offset, 2, ENC_BIG_ENDIAN, &nsh_bytes_len);
nsh_bytes_len *= 4;
proto_item_set_len(ti, nsh_bytes_len);
/* Bits 4 - 9 are reserved */
proto_tree_add_item(nsh_tree, hf_nsh_reservedbits, tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(nsh_tree, hf_nsh_length, tvb, offset, 2, ENC_BIG_ENDIAN);
md_type = tvb_get_guint8(tvb, offset + 2);
proto_tree_add_item(nsh_tree, hf_nsh_md_type, tvb, offset + 2, 1, ENC_BIG_ENDIAN);
nsh_next_proto = tvb_get_guint8(tvb, offset + 3);
proto_tree_add_item(nsh_tree, hf_nsh_next_proto, tvb, offset + 3, 1, ENC_BIG_ENDIAN);
md_type = tvb_get_guint8(tvb, offset + 2);
proto_tree_add_item(nsh_tree, hf_nsh_md_type, tvb, offset + 2, 1, ENC_BIG_ENDIAN);
/*NSH Service Path Header */
offset = offset + 4;
proto_tree_add_item(nsh_tree, hf_nsh_service_pathID, tvb, offset, 3, ENC_BIG_ENDIAN);
proto_tree_add_item(nsh_tree, hf_nsh_service_index, tvb, offset + 3, 1, ENC_BIG_ENDIAN);
nsh_next_proto = tvb_get_guint8(tvb, offset + 3);
proto_tree_add_item(nsh_tree, hf_nsh_next_proto, tvb, offset + 3, 1, ENC_BIG_ENDIAN);
/*NSH Service Path Header */
offset = offset + 4;
proto_tree_add_item(nsh_tree, hf_nsh_service_pathID, tvb, offset, 3, ENC_BIG_ENDIAN);
proto_tree_add_item(nsh_tree, hf_nsh_service_index, tvb, offset + 3, 1, ENC_BIG_ENDIAN);
/* Decode Context Headers */
offset = offset + 4;
switch (md_type) {
case MD_TYPE_1:
dissect_nsh_md_type_1(tvb, nsh_tree, offset);
break;
case MD_TYPE_2:
/* MD Type 2 indicates ZERO or more Variable Length Context headers*/
if (nsh_bytes_len > 8)
dissect_nsh_md_type_2(tvb, nsh_tree, offset, nsh_bytes_len);
break;
/* Decode Context Headers */
offset = offset + 4;
switch (md_type) {
case MD_TYPE_1:
/* The Length MUST be of value 0x6 for MD Type equal to 0x1 */
if (nsh_bytes_len != 4 * 6) {
expert_add_info_format(pinfo, length_pi, &ei_nsh_length_invalid,
"Length MUST be of value 0x6 for MD Type equal to 0x1");
nsh_bytes_len = 4 * 6;
}
dissect_nsh_md_type_1(tvb, nsh_tree, offset);
break;
/*Decode next protocol payload */
case MD_TYPE_2:
if (captured_length > (nsh_bytes_len)) {
/* The Length MUST be of value 0x2 or greater for MD Type equal to 0x2 */
if (nsh_bytes_len < 4 * 2) {
expert_add_info_format(pinfo, length_pi, &ei_nsh_length_invalid,
"Length MUST be of value 0x2 or greater for MD Type equal to 0x2");
nsh_bytes_len = 4 * 2;
}
/* MD Type 2 indicates ZERO or more Variable Length Context headers*/
if (nsh_bytes_len > 8)
dissect_nsh_md_type_2(tvb, nsh_tree, offset, nsh_bytes_len);
break;
next_tvb = tvb_new_subset_remaining(tvb, nsh_bytes_len);
if (!dissector_try_uint(subdissector_table, nsh_next_proto, tvb, pinfo, tree)) {
call_data_dissector(next_tvb, pinfo, tree);
}
default:
/*
* Unknown type, but assume presence of at least the NSH
* Base Header (32 bits, 4 bytes).
*/
if (nsh_bytes_len < 4) {
expert_add_info_format(pinfo, length_pi, &ei_nsh_length_invalid,
"Length must be at least 0x1 for NSH Base Header");
nsh_bytes_len = 4;
}
break;
}
/*Decode next protocol payload */
if (tvb_captured_length_remaining(tvb, nsh_bytes_len) > 0) {
next_tvb = tvb_new_subset_remaining(tvb, nsh_bytes_len);
if (!dissector_try_uint(subdissector_table, nsh_next_proto, next_tvb, pinfo, tree)) {
call_data_dissector(next_tvb, pinfo, tree);
}
}
@ -212,6 +232,8 @@ dissect_nsh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
void
proto_register_nsh(void)
{
expert_module_t *expert_nsh;
static hf_register_info nsh_info[] = {
/* Network Service Header fields */
@ -326,10 +348,17 @@ proto_register_nsh(void)
&ett_nsh,
};
static ei_register_info ei[] = {
{ &ei_nsh_length_invalid, { "nsh.length.invalid", PI_PROTOCOL, PI_WARN, "Invalid total length", EXPFILL }},
};
proto_nsh = proto_register_protocol("Network Service Header", "NSH", "nsh");
proto_register_field_array(proto_nsh, nsh_info, array_length(nsh_info));
proto_register_subtree_array(ett, array_length(ett));
expert_nsh = expert_register_protocol(proto_nsh);
expert_register_field_array(expert_nsh, ei, array_length(ei));
subdissector_table = register_dissector_table("nsh.next_proto", "NSH Next Protocol", proto_nsh, FT_UINT32, BASE_DEC);
}