Skip to content

Commit

Permalink
decode: limits the number of decoded layers
Browse files Browse the repository at this point in the history
so as to avoid overrecursion leading to stack exhaustion

(cherry picked from commit 7500c29)
  • Loading branch information
catenacyber authored and victorjulien committed Feb 27, 2021
1 parent 0495be2 commit 82a8124
Show file tree
Hide file tree
Showing 19 changed files with 93 additions and 5 deletions.
4 changes: 3 additions & 1 deletion rules/decoder-events.rules
Expand Up @@ -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

3 changes: 3 additions & 0 deletions src/decode-chdlc.c
Expand Up @@ -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))
Expand Down
3 changes: 3 additions & 0 deletions src/decode-erspan.c
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/decode-ethernet.c
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/decode-events.c
Expand Up @@ -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",
Expand Down
5 changes: 4 additions & 1 deletion src/decode-events.h
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions src/decode-geneve.c
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/decode-gre.c
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions src/decode-ipv4.c
Expand Up @@ -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");
Expand Down
3 changes: 3 additions & 0 deletions src/decode-ipv6.c
Expand Up @@ -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)) {
Expand Down
3 changes: 3 additions & 0 deletions src/decode-mpls.c
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/decode-ppp.c
Expand Up @@ -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))
Expand Down
3 changes: 3 additions & 0 deletions src/decode-sll.c
Expand Up @@ -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))
Expand Down
9 changes: 9 additions & 0 deletions src/decode-template.c
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/decode-vlan.c
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/decode-vxlan.c
Expand Up @@ -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)
Expand Down
16 changes: 14 additions & 2 deletions src/decode.c
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
}
}

/**
Expand Down
20 changes: 19 additions & 1 deletion src/decode.h
Expand Up @@ -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
Expand Down Expand Up @@ -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 { \
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions suricata.yaml.in
Expand Up @@ -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
##
Expand Down

0 comments on commit 82a8124

Please sign in to comment.