From: Philippe Antoine Date: Thu, 28 Jan 2021 16:48:48 +0000 (+0100) Subject: decode: limits the number of decoded layers X-Git-Tag: suricata-7.0.0-beta1~1760 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7500c29300dcef8716d87461842e7d7c3e5101ac;p=thirdparty%2Fsuricata.git decode: limits the number of decoded layers so as to avoid overrecursion leading to stack exhaustion --- 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 a7ed61869d..4607eb0a1c 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-esp.c b/src/decode-esp.c index 575035e793..2057679675 100644 --- a/src/decode-esp.c +++ b/src/decode-esp.c @@ -61,6 +61,9 @@ int DecodeESP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *p { StatsIncr(tv, dtv->counter_esp); + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } if (unlikely(DecodeESPPacket(tv, p, pkt, len) < 0)) { CLEAR_ESP_PACKET(p); return TM_ECODE_FAILED; 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 ee8fa24b75..286d9a902f 100644 --- a/src/decode-events.c +++ b/src/decode-events.c @@ -566,6 +566,10 @@ const struct DecodeEvents_ DEvents[] = { "decoder.nsh.unknown_payload", NSH_UNKNOWN_PAYLOAD, }, + { + "decoder.too_many_layers", + GENERIC_TOO_MANY_LAYERS, + }, /* STREAM EVENTS */ { diff --git a/src/decode-events.h b/src/decode-events.h index c34ae80ac4..31fedc7e2d 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -211,8 +211,11 @@ enum { NSH_UNSUPPORTED_TYPE, NSH_UNKNOWN_PAYLOAD, + /* generic events */ + GENERIC_TOO_MANY_LAYERS, + /* END OF DECODE EVENTS ON SINGLE PACKET */ - DECODE_EVENT_PACKET_MAX = NSH_UNKNOWN_PAYLOAD, + 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 a55352a733..078b9bfae5 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 60bb9e63d9..171b7d636e 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 05ef92d1bd..22e9b5d7d0 100644 --- a/src/decode-ipv6.c +++ b/src/decode-ipv6.c @@ -565,6 +565,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-nsh.c b/src/decode-nsh.c index 7a3355ddb1..f3dd542ee6 100644 --- a/src/decode-nsh.c +++ b/src/decode-nsh.c @@ -51,6 +51,9 @@ int DecodeNSH(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *p ENGINE_SET_INVALID_EVENT(p, NSH_HEADER_TOO_SMALL); return TM_ECODE_FAILED; } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } /* Sanity check the header version */ const NshHdr *hdr = (const NshHdr *)pkt; 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 384a7d94c2..9e4c33b41e 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: @@ -764,6 +768,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 ea2613ecb7..d5d7d827f7 100644 --- a/src/decode.h +++ b/src/decode.h @@ -610,6 +610,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 @@ -831,6 +836,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 { \ @@ -952,7 +958,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); @@ -1161,6 +1166,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 c1e6c207f5..728d3e340f 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 ##