]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
decode: limits the number of decoded layers
authorPhilippe Antoine <contact@catenacyber.fr>
Thu, 28 Jan 2021 16:48:48 +0000 (17:48 +0100)
committerVictor Julien <victor@inliniac.net>
Sat, 27 Feb 2021 16:50:18 +0000 (17:50 +0100)
so as to avoid overrecursion leading to stack exhaustion

(cherry picked from commit 7500c29300dcef8716d87461842e7d7c3e5101ac)

19 files changed:
rules/decoder-events.rules
src/decode-chdlc.c
src/decode-erspan.c
src/decode-ethernet.c
src/decode-events.c
src/decode-events.h
src/decode-geneve.c
src/decode-gre.c
src/decode-ipv4.c
src/decode-ipv6.c
src/decode-mpls.c
src/decode-ppp.c
src/decode-sll.c
src/decode-template.c
src/decode-vlan.c
src/decode-vxlan.c
src/decode.c
src/decode.h
suricata.yaml.in

index d5e001f9c71299c66e2343e45f3e5c2fe73b04e7..661fb13a1e667afd52e79ba1f795f1c5d067cfe9 100644 (file)
@@ -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
 
index 6ecb6b44bad41def2d0da7d5b94bf8e442290244..55b4e0329b4feb683309af9cc38b09121b91febb 100644 (file)
@@ -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))
index c70b1a32aa160ce40b2c538347cef5a1925753c0..ab326a0d626f1e3490bf541ea3f085b3e3366c5f 100644 (file)
@@ -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;
index e46c09cd5f2c00304982928359f143bd5fdd7bfa..556f5ed338882f4b72a9b94de4ddd753de0333ed 100644 (file)
@@ -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;
index 3f18aabf9a24a8259399516df03d9f5f8d576587..4f23197ee542e67a586cb4c093adec7e8062b2e8 100644 (file)
@@ -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",
index 434dae1391da5643cb2e432c0015fe59e6741bf9..aae54e1291a335a7c193d3a6a92c4971169a321f 100644 (file)
@@ -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,
index 41ed3ebcf7c2f92c8feb9bd998cf69e922f79385..88c6ade8259dad743c713f67cba5dab29da9a7fe 100644 (file)
@@ -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);
index 058c529b85190c6d36d5ac6bb18be93b77fa5309..04d03266c258081f34fab50b37d21117008d5bd4 100644 (file)
@@ -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)
index 5d9d12a8b93e31a7ed0c82592a12be9975b33588..825200500f3ad8d5756b235486f95d887178664b 100644 (file)
@@ -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");
index b20440145ac74ebd8985dbed15ef38850e08b189..e953419e0f67c9c9fa3c5843210e62dbb7312c8f 100644 (file)
@@ -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)) {
index e64df63b7f77c55b41138ae06f1cdfba856fc7bc..6de6e49e2d08ccf8b9addff8d0522b9b5b339f61 100644 (file)
@@ -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);
index 7a865c5d487bca9ecf4cc8f583f987a3d13efb0c..7cb311c4f5322a156065fd34fa11d8a3f24a3b3e 100644 (file)
@@ -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))
index 9e02b1c8c5af482cd0af4b7c695b4ff7488973e5..7bfe5799d2245c6863aa21277ee1532b10757e48 100644 (file)
@@ -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))
index f47c736929b3bb273386458be4d903ece838d6db..15091df78aaa860c19ab86a217f8284ffb38f00d 100644 (file)
@@ -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;
index d929bae826a8d098ced1cbd3655a0e05e38bca8a..aba0ec93fc1c381f83c069ed42f4d06d1961dea0 100644 (file)
@@ -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;
index dc952a8a12a3a7f63087326fbfdfd4a6e8376980..f5d754de0951208646bb3e34fc8c2789a9fa80c4 100644 (file)
@@ -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)
index 0c0ca72f1d493b08a49d476c102e0261544be2f7..4d54e5ecbc2380a4ed07140588b8104d66f1e5ef 100644 (file)
@@ -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;
+        }
+    }
 }
 
 /**
index fc298d94bd66b7cce0ff555d92386795dcbd86a9..82b70f9610158f2abf054d8963b38d74a8fbc5c7 100644 (file)
@@ -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,
index 8534f7ee9d5a0ffac13a81e48b262460cbb02454..ac6b5341f52b5345c47062846b23388e7bba3df3 100644 (file)
@@ -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
 ##