From: Eric Leblond Date: Fri, 24 Nov 2017 15:59:34 +0000 (+0100) Subject: app-layer-ftp: add memcap for ftp X-Git-Tag: suricata-4.1.0-beta1~424 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=711b6fb389d36f10afc72450cc2ae0b81d4f6935;p=thirdparty%2Fsuricata.git app-layer-ftp: add memcap for ftp Add a memory cap for the FTP protocol. --- diff --git a/src/app-layer-ftp.c b/src/app-layer-ftp.c index b080eb77bd..d13cb914f6 100644 --- a/src/app-layer-ftp.c +++ b/src/app-layer-ftp.c @@ -54,11 +54,140 @@ #include "util-memcmp.h" #include "util-memrchr.h" #include "util-byte.h" +#include "util-mem.h" +#include "util-misc.h" #ifdef HAVE_RUST #include "rust-ftp-mod-gen.h" #endif +uint64_t ftp_config_memcap = 0; + +SC_ATOMIC_DECLARE(uint64_t, ftp_memuse); +SC_ATOMIC_DECLARE(uint64_t, ftp_memcap); + +static void FTPParseMemcap(void) +{ + const char *conf_val; + + /** set config values for memcap, prealloc and hash_size */ + if ((ConfGet("app-layer.protocols.ftp.memcap", &conf_val)) == 1) + { + if (ParseSizeStringU64(conf_val, &ftp_config_memcap) < 0) { + SCLogError(SC_ERR_SIZE_PARSE, "Error parsing ftp.memcap " + "from conf file - %s. Killing engine", + conf_val); + exit(EXIT_FAILURE); + } + SCLogInfo("FTP memcap: %"PRIu64, ftp_config_memcap); + } else { + /* default to unlimited */ + ftp_config_memcap = 0; + } + + SC_ATOMIC_INIT(ftp_memuse); + SC_ATOMIC_INIT(ftp_memcap); +} + +static void FTPIncrMemuse(uint64_t size) +{ + (void) SC_ATOMIC_ADD(ftp_memuse, size); + return; +} + +static void FTPDecrMemuse(uint64_t size) +{ + (void) SC_ATOMIC_SUB(ftp_memuse, size); + return; +} + +uint64_t FTPMemuseGlobalCounter(void) +{ + uint64_t tmpval = SC_ATOMIC_GET(ftp_memuse); + return tmpval; +} + +uint64_t FTPMemcapGlobalCounter(void) +{ + uint64_t tmpval = SC_ATOMIC_GET(ftp_memcap); + return tmpval; +} + +/** + * \brief Check if alloc'ing "size" would mean we're over memcap + * + * \retval 1 if in bounds + * \retval 0 if not in bounds + */ +static int FTPCheckMemcap(uint64_t size) +{ + if (ftp_config_memcap == 0 || size + SC_ATOMIC_GET(ftp_memuse) <= ftp_config_memcap) + return 1; + (void) SC_ATOMIC_ADD(ftp_memcap, 1); + return 0; +} + +static void *FTPMalloc(size_t size) +{ + void *ptr = NULL; + + if (FTPCheckMemcap((uint32_t)size) == 0) + return NULL; + + ptr = SCMalloc(size); + + if (unlikely(ptr == NULL)) + return NULL; + + FTPIncrMemuse((uint64_t)size); + + return ptr; +} + +static void *FTPCalloc(size_t n, size_t size) +{ + void *ptr = NULL; + + if (FTPCheckMemcap((uint32_t)(n * size)) == 0) + return NULL; + + ptr = SCCalloc(n, size); + + if (unlikely(ptr == NULL)) + return NULL; + + FTPIncrMemuse((uint64_t)(n * size)); + + return ptr; +} + +static void *FTPRealloc(void *ptr, size_t orig_size, size_t size) +{ + void *rptr = NULL; + + if (FTPCheckMemcap((uint32_t)(size - orig_size)) == 0) + return NULL; + + rptr = SCRealloc(ptr, size); + if (rptr == NULL) + return NULL; + + if (size > orig_size) { + FTPIncrMemuse(size - orig_size); + } else { + FTPDecrMemuse(orig_size - size); + } + + return rptr; +} + +static void FTPFree(void *ptr, size_t size) +{ + SCFree(ptr); + + FTPDecrMemuse((uint64_t)size); +} + static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state) { void *ptmp; @@ -68,7 +197,7 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state) line_state->current_line_lf_seen = 0; if (line_state->current_line_db == 1) { line_state->current_line_db = 0; - SCFree(line_state->db); + FTPFree(line_state->db, line_state->db_len); line_state->db = NULL; line_state->db_len = 0; state->current_line = NULL; @@ -86,7 +215,7 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state) * if we see fragmentation then it's definitely something you * should alert about */ if (line_state->current_line_db == 0) { - line_state->db = SCMalloc(state->input_len); + line_state->db = FTPMalloc(state->input_len); if (line_state->db == NULL) { return -1; } @@ -94,10 +223,10 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state) memcpy(line_state->db, state->input, state->input_len); line_state->db_len = state->input_len; } else { - ptmp = SCRealloc(line_state->db, + ptmp = FTPRealloc(line_state->db, line_state->db_len, (line_state->db_len + state->input_len)); if (ptmp == NULL) { - SCFree(line_state->db); + FTPFree(line_state->db, line_state->db_len); line_state->db = NULL; line_state->db_len = 0; return -1; @@ -117,10 +246,10 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state) line_state->current_line_lf_seen = 1; if (line_state->current_line_db == 1) { - ptmp = SCRealloc(line_state->db, + ptmp = FTPRealloc(line_state->db, line_state->db_len, (line_state->db_len + (lf_idx + 1 - state->input))); if (ptmp == NULL) { - SCFree(line_state->db); + FTPFree(line_state->db, line_state->db_len); line_state->db = NULL; line_state->db_len = 0; return -1; @@ -240,7 +369,7 @@ static void FtpTransferCmdFree(void *data) if (cmd->file_name) { SCFree(cmd->file_name); } - SCFree(cmd); + FTPFree(cmd, sizeof(struct FtpTransferCmd)); } /** @@ -281,9 +410,10 @@ static int FTPParseRequest(Flow *f, void *ftp_state, switch (state->command) { case FTP_COMMAND_PORT: if (state->current_line_len > state->port_line_size) { - ptmp = SCRealloc(state->port_line, state->current_line_len); + ptmp = FTPRealloc(state->port_line, state->port_line_size, + state->current_line_len); if (ptmp == NULL) { - SCFree(state->port_line); + FTPFree(state->port_line, state->port_line_size); state->port_line = NULL; state->port_line_size = 0; return 0; @@ -308,14 +438,14 @@ static int FTPParseRequest(Flow *f, void *ftp_state, if (state->dyn_port == 0) { SCReturnInt(-1); } - struct FtpTransferCmd *data = SCCalloc(1, sizeof(struct FtpTransferCmd)); + struct FtpTransferCmd *data = FTPCalloc(1, sizeof(struct FtpTransferCmd)); if (data == NULL) SCReturnInt(-1); data->DFree = FtpTransferCmdFree; /* Min size has been checked in FTPParseRequestCommand */ - data->file_name = SCCalloc(state->current_line_len - 4, sizeof(char)); + data->file_name = FTPCalloc(state->current_line_len - 4, sizeof(char)); if (data->file_name == NULL) { - SCFree(data); + FTPFree(data, sizeof(struct FtpTransferCmd)); SCReturnInt(-1); } data->file_name[state->current_line_len - 5] = 0; @@ -327,7 +457,7 @@ static int FTPParseRequest(Flow *f, void *ftp_state, state->dyn_port, ALPROTO_FTPDATA, data); if (ret == -1) { - SCFree(data); + FTPFree(data, sizeof(struct FtpTransferCmd)); SCLogDebug("No expectation created."); SCReturnInt(-1); } else { @@ -443,7 +573,7 @@ static uint64_t ftp_state_memcnt = 0; static void *FTPStateAlloc(void) { - void *s = SCMalloc(sizeof(FtpState)); + void *s = FTPMalloc(sizeof(FtpState)); if (unlikely(s == NULL)) return NULL; @@ -462,11 +592,11 @@ static void FTPStateFree(void *s) { FtpState *fstate = (FtpState *) s; if (fstate->port_line != NULL) - SCFree(fstate->port_line); + FTPFree(fstate->port_line, fstate->port_line_size); if (fstate->line_state[0].db) - SCFree(fstate->line_state[0].db); + FTPFree(fstate->line_state[0].db, fstate->line_state[0].db_len); if (fstate->line_state[1].db) - SCFree(fstate->line_state[1].db); + FTPFree(fstate->line_state[1].db, fstate->line_state[1].db_len); //AppLayerDecoderEventsFreeEvents(&s->decoder_events); @@ -474,7 +604,7 @@ static void FTPStateFree(void *s) DetectEngineStateFree(fstate->de_state); } - SCFree(s); + FTPFree(s, sizeof(FtpState)); #ifdef DEBUG SCMutexLock(&ftp_state_mem_lock); ftp_state_memcnt--; @@ -678,7 +808,7 @@ static uint64_t ftpdata_state_memcnt = 0; static void *FTPDataStateAlloc(void) { - void *s = SCMalloc(sizeof(FtpDataState)); + void *s = FTPMalloc(sizeof(FtpDataState)); if (unlikely(s == NULL)) return NULL; @@ -702,7 +832,7 @@ static void FTPDataStateFree(void *s) DetectEngineStateFree(fstate->de_state); } if (fstate->file_name != NULL) { - SCFree(fstate->file_name); + FTPFree(fstate->file_name, fstate->file_len); } FileContainerFree(fstate->files); @@ -831,7 +961,12 @@ void RegisterFTPParsers(void) FTPDataGetAlstateProgressCompletionStatus); sbcfg.buf_size = 4096; + sbcfg.Malloc = FTPMalloc; + sbcfg.Calloc = FTPCalloc; + sbcfg.Realloc = FTPRealloc; + sbcfg.Free = FTPFree; + FTPParseMemcap(); } else { SCLogInfo("Parsed disabled for %s protocol. Protocol detection" "still on.", proto_name); diff --git a/src/app-layer-ftp.h b/src/app-layer-ftp.h index b74f141763..84373a8662 100644 --- a/src/app-layer-ftp.h +++ b/src/app-layer-ftp.h @@ -160,6 +160,8 @@ typedef struct FtpDataState_ { void RegisterFTPParsers(void); void FTPParserRegisterTests(void); void FTPAtExitPrintStats(void); +uint64_t FTPMemuseGlobalCounter(void); +uint64_t FTPMemcapGlobalCounter(void); #ifdef HAVE_LIBJANSSON json_t *JsonFTPDataAddMetadata(const Flow *f); diff --git a/src/app-layer.c b/src/app-layer.c index 66cd13a12d..5366db762e 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -30,6 +30,7 @@ #include "app-layer-parser.h" #include "app-layer-protos.h" #include "app-layer-expectation.h" +#include "app-layer-ftp.h" #include "app-layer-detect-proto.h" #include "stream-tcp-reassemble.h" #include "stream-tcp-private.h" @@ -833,6 +834,8 @@ void AppLayerRegisterGlobalCounters(void) StatsRegisterGlobalCounter("dns.memcap_global", DNSMemcapGetMemcapGlobalCounter); StatsRegisterGlobalCounter("http.memuse", HTPMemuseGlobalCounter); StatsRegisterGlobalCounter("http.memcap", HTPMemcapGlobalCounter); + StatsRegisterGlobalCounter("ftp.memuse", FTPMemuseGlobalCounter); + StatsRegisterGlobalCounter("ftp.memcap", FTPMemcapGlobalCounter); StatsRegisterGlobalCounter("app_layer.expectations", ExpectationGetCounter); } diff --git a/suricata.yaml.in b/suricata.yaml.in index e0669e6755..eb89c8c428 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -705,6 +705,7 @@ app-layer: enabled: yes ftp: enabled: yes + # memcap: 64mb ssh: enabled: yes smtp: