diff --git a/rules/decoder-events.rules b/rules/decoder-events.rules index d5e001f9c7..661fb13a1e 100644 --- a/rules/decoder-events.rules +++ b/rules/decoder-events.rules @@ -146,5 +146,7 @@ alert pkthdr any any -> any any (msg:"SURICATA DCE packet too small"; decode-eve # Cisco HDLC alert pkthdr any any -> any any (msg:"SURICATA CHDLC packet too small"; decode-event:chdlc.pkt_too_small; classtype:protocol-command-decode; sid:2200115; rev:1;) -# next sid is 2200116 +alert pkthdr any any -> any any (msg:"SURICATA packet with too many layers"; decode-event:too_many_layers; classtype:protocol-command-decode; sid:2200116; rev:1;) + +# next sid is 2200117 diff --git a/src/decode-chdlc.c b/src/decode-chdlc.c index 6ecb6b44ba..55b4e0329b 100644 --- a/src/decode-chdlc.c +++ b/src/decode-chdlc.c @@ -51,6 +51,9 @@ int DecodeCHDLC(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, if (unlikely(len > CHDLC_HEADER_LEN + USHRT_MAX)) { return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } CHDLCHdr *hdr = (CHDLCHdr *)pkt; if (unlikely(hdr == NULL)) diff --git a/src/decode-erspan.c b/src/decode-erspan.c index c70b1a32aa..ab326a0d62 100644 --- a/src/decode-erspan.c +++ b/src/decode-erspan.c @@ -80,6 +80,9 @@ int DecodeERSPAN(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t ENGINE_SET_EVENT(p,ERSPAN_HEADER_TOO_SMALL); return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } const ErspanHdr *ehdr = (const ErspanHdr *)pkt; uint16_t version = SCNtohs(ehdr->ver_vlan) >> 12; diff --git a/src/decode-ethernet.c b/src/decode-ethernet.c index e46c09cd5f..556f5ed338 100644 --- a/src/decode-ethernet.c +++ b/src/decode-ethernet.c @@ -48,6 +48,9 @@ int DecodeEthernet(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } p->ethh = (EthernetHdr *)pkt; if (unlikely(p->ethh == NULL)) return TM_ECODE_FAILED; diff --git a/src/decode-events.c b/src/decode-events.c index 3f18aabf9a..4f23197ee5 100644 --- a/src/decode-events.c +++ b/src/decode-events.c @@ -535,6 +535,11 @@ const struct DecodeEvents_ DEvents[] = { CHDLC_PKT_TOO_SMALL, }, + { + "decoder.too_many_layers", + GENERIC_TOO_MANY_LAYERS, + }, + /* STREAM EVENTS */ { "stream.3whs_ack_in_wrong_dir", diff --git a/src/decode-events.h b/src/decode-events.h index 434dae1391..aae54e1291 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -200,8 +200,11 @@ enum { /* Cisco HDLC events. */ CHDLC_PKT_TOO_SMALL, + /* generic events */ + GENERIC_TOO_MANY_LAYERS, + /* END OF DECODE EVENTS ON SINGLE PACKET */ - DECODE_EVENT_PACKET_MAX = CHDLC_PKT_TOO_SMALL, + DECODE_EVENT_PACKET_MAX = GENERIC_TOO_MANY_LAYERS, /* STREAM EVENTS */ STREAM_3WHS_ACK_IN_WRONG_DIR, diff --git a/src/decode-geneve.c b/src/decode-geneve.c index 41ed3ebcf7..88c6ade825 100644 --- a/src/decode-geneve.c +++ b/src/decode-geneve.c @@ -194,6 +194,9 @@ int DecodeGeneve(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t if (unlikely(len < GENEVE_MIN_HEADER_LEN)) return TM_ECODE_FAILED; + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } /* Specific Geneve header field validation */ geneve_hdr_len = GENEVE_TOTAL_HEADER_LEN(geneve_hdr); diff --git a/src/decode-gre.c b/src/decode-gre.c index 058c529b85..04d03266c2 100644 --- a/src/decode-gre.c +++ b/src/decode-gre.c @@ -54,6 +54,9 @@ int DecodeGRE(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *p ENGINE_SET_INVALID_EVENT(p, GRE_PKT_TOO_SMALL); return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } p->greh = (GREHdr *)pkt; if(p->greh == NULL) diff --git a/src/decode-ipv4.c b/src/decode-ipv4.c index 5d9d12a8b9..825200500f 100644 --- a/src/decode-ipv4.c +++ b/src/decode-ipv4.c @@ -521,6 +521,9 @@ int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, SCLogDebug("pkt %p len %"PRIu16"", pkt, len); + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } /* do the actual decoding */ if (unlikely(DecodeIPV4Packet (p, pkt, len) < 0)) { SCLogDebug("decoding IPv4 packet failed"); diff --git a/src/decode-ipv6.c b/src/decode-ipv6.c index b20440145a..e953419e0f 100644 --- a/src/decode-ipv6.c +++ b/src/decode-ipv6.c @@ -581,6 +581,9 @@ int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t * { StatsIncr(tv, dtv->counter_ipv6); + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } /* do the actual decoding */ int ret = DecodeIPV6Packet (tv, dtv, p, pkt, len); if (unlikely(ret < 0)) { diff --git a/src/decode-mpls.c b/src/decode-mpls.c index e64df63b7f..6de6e49e2d 100644 --- a/src/decode-mpls.c +++ b/src/decode-mpls.c @@ -53,6 +53,9 @@ int DecodeMPLS(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, StatsIncr(tv, dtv->counter_mpls); + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } do { if (len < MPLS_HEADER_LEN) { ENGINE_SET_INVALID_EVENT(p, MPLS_HEADER_TOO_SMALL); diff --git a/src/decode-ppp.c b/src/decode-ppp.c index 7a865c5d48..7cb311c4f5 100644 --- a/src/decode-ppp.c +++ b/src/decode-ppp.c @@ -49,6 +49,9 @@ int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL); return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } p->ppph = (PPPHdr *)pkt; if (unlikely(p->ppph == NULL)) diff --git a/src/decode-sll.c b/src/decode-sll.c index 9e02b1c8c5..7bfe5799d2 100644 --- a/src/decode-sll.c +++ b/src/decode-sll.c @@ -45,6 +45,9 @@ int DecodeSll(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, ENGINE_SET_INVALID_EVENT(p, SLL_PKT_TOO_SMALL); return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } SllHdr *sllh = (SllHdr *)pkt; if (unlikely(sllh == NULL)) diff --git a/src/decode-template.c b/src/decode-template.c index f47c736929..15091df78a 100644 --- a/src/decode-template.c +++ b/src/decode-template.c @@ -62,6 +62,15 @@ int DecodeTEMPLATE(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, //ENGINE_SET_EVENT(p,TEMPLATE_HEADER_TOO_SMALL); return TM_ECODE_FAILED; } + /* Each packet keeps a count of decoded layers + * This function increases it and returns false + * if we have too many decoded layers, such as + * ethernet/MPLS/ethernet/MPLS... which may + * lead to stack overflow by a too deep recursion + */ + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } /* Now we can access the header */ const TemplateHdr *hdr = (const TemplateHdr *)pkt; diff --git a/src/decode-vlan.c b/src/decode-vlan.c index d929bae826..aba0ec93fc 100644 --- a/src/decode-vlan.c +++ b/src/decode-vlan.c @@ -70,6 +70,9 @@ int DecodeVLAN(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, ENGINE_SET_INVALID_EVENT(p, VLAN_HEADER_TOO_SMALL); return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } if (p->vlan_idx >= 2) { ENGINE_SET_EVENT(p,VLAN_HEADER_TOO_MANY_LAYERS); return TM_ECODE_FAILED; diff --git a/src/decode-vxlan.c b/src/decode-vxlan.c index dc952a8a12..f5d754de09 100644 --- a/src/decode-vxlan.c +++ b/src/decode-vxlan.c @@ -136,6 +136,9 @@ int DecodeVXLAN(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, if (len < (VXLAN_HEADER_LEN + sizeof(EthernetHdr))) return TM_ECODE_FAILED; + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } const VXLANHeader *vxlanh = (const VXLANHeader *)pkt; if ((vxlanh->flags[0] & 0x08) == 0 || vxlanh->res != 0) diff --git a/src/decode.c b/src/decode.c index 0c0ca72f1d..4d54e5ecbc 100644 --- a/src/decode.c +++ b/src/decode.c @@ -72,9 +72,13 @@ uint32_t default_packet_size = 0; extern bool stats_decoder_events; extern const char *stats_decoder_events_prefix; extern bool stats_stream_events; +uint8_t decoder_max_layers = PKT_DEFAULT_MAX_DECODED_LAYERS; -int DecodeTunnel(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, - const uint8_t *pkt, uint32_t len, enum DecodeTunnelProto proto) +static int DecodeTunnel(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t, + enum DecodeTunnelProto) WARN_UNUSED; + +static int DecodeTunnel(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, + uint32_t len, enum DecodeTunnelProto proto) { switch (proto) { case DECODE_TUNNEL_PPP: @@ -760,6 +764,14 @@ void DecodeGlobalConfig(void) DecodeGeneveConfig(); DecodeVXLANConfig(); DecodeERSPANConfig(); + intmax_t value = 0; + if (ConfGetInt("decoder.max-layers", &value) == 1) { + if (value < 0 || value > UINT8_MAX) { + SCLogWarning(SC_ERR_INVALID_VALUE, "Invalid value for decoder.max-layers"); + } else { + decoder_max_layers = value; + } + } } /** diff --git a/src/decode.h b/src/decode.h index fc298d94bd..82b70f9610 100644 --- a/src/decode.h +++ b/src/decode.h @@ -607,6 +607,11 @@ typedef struct Packet_ */ struct PktPool_ *pool; + /* count decoded layers of packet : too many layers + * cause issues with performance and stability (stack exhaustion) + */ + uint8_t nb_decoded_layers; + #ifdef PROFILING PktProfiling *profile; #endif @@ -822,6 +827,7 @@ void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s); PACKET_RESET_CHECKSUMS((p)); \ PACKET_PROFILING_RESET((p)); \ p->tenant_id = 0; \ + p->nb_decoded_layers = 0; \ } while (0) #define PACKET_RECYCLE(p) do { \ @@ -942,7 +948,6 @@ int DecodeSll(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint3 int DecodePPP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodePPPOESession(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodePPPOEDiscovery(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); -int DecodeTunnel(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t, enum DecodeTunnelProto) WARN_UNUSED; int DecodeNull(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodeRaw(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodeIPV4(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t); @@ -1149,6 +1154,19 @@ void DecodeUnregisterCounters(void); #define PKT_SET_SRC(p, src_val) ((p)->pkt_src = src_val) +#define PKT_DEFAULT_MAX_DECODED_LAYERS 16 +extern uint8_t decoder_max_layers; + +static inline bool PacketIncreaseCheckLayers(Packet *p) +{ + p->nb_decoded_layers++; + if (p->nb_decoded_layers >= decoder_max_layers) { + ENGINE_SET_INVALID_EVENT(p, GENERIC_TOO_MANY_LAYERS); + return false; + } + return true; +} + /** \brief return true if *this* packet needs to trigger a verdict. * * If we have the root packet, and we have none outstanding, diff --git a/suricata.yaml.in b/suricata.yaml.in index 8534f7ee9d..ac6b5341f5 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1361,6 +1361,9 @@ decoder: enabled: true ports: $GENEVE_PORTS # syntax: '[6081, 1234]' or '6081'. + # maximum number of decoder layers for a packet + # max-layers: 16 + ## ## Performance tuning and profiling ##