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:
parent
a9be088128
commit
ce8863c6ef
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue