From fcebdc32f26cb89cbe74105c86a7e08cb810fa8f Mon Sep 17 00:00:00 2001 From: "Russ Combs (rucombs)" Date: Sat, 9 Mar 2019 19:47:51 -0500 Subject: [PATCH] Merge pull request #1539 in SNORT/snort3 from ~RUCOMBS/snort3:memory_misery to master Squashed commit of the following: commit 29f8a2c133f0aa5726c2d7a53f164bc840c069a5 Author: russ Date: Sat Mar 9 19:45:19 2019 -0500 build: fix override warning commit 322dac9242dc6b1a0c1c1cfd0289899fdca9e158 Author: russ Date: Sat Mar 9 12:52:28 2019 -0500 memory: add configurable L3/L4 specific weights for better estimation against cap commit 30826c6c6d425a24aedd49d7b1375580a449b027 Author: russ Date: Fri Mar 8 21:26:05 2019 -0500 stream_tcp: patch around premature application of delayed actions that yoink the seglist commit a6b3a0f313ad2f6911cc0167cca0e0179aedba4f Author: russ Date: Thu Mar 7 03:37:08 2019 -0500 stream: purge remaining flows before shutdown counts commit a22cb207099c52b9bb0b3af7c2b2c45798f15213 Author: russ Date: Thu Mar 7 03:12:09 2019 -0500 stream_tcp: implement reserve seglist commit fcad14fd2875f9b5f3c6c792882617529dea67f3 Author: russ Date: Thu Mar 7 01:53:07 2019 -0500 stream_tcp: consolidate segment node and data commit 38d2075e51809c564057dde52b9ea47913b29f9d Author: russ Date: Wed Mar 6 16:28:26 2019 -0500 memory: require subclass implementation of FlowData::size_of() commit 5cd42d4fa5a3f30d1e2f1a0008134403998e8779 Author: russ Date: Wed Mar 6 16:05:38 2019 -0500 memory: add size_of to various FlowData subclasses commit 3b82fc157d789d993eb8d7d1c77c05898956da6c Author: russ Date: Wed Mar 6 16:04:45 2019 -0500 memory: apply fudge factor to tracking to better align with RSS commit 2deb67a92ddc1f8143d3e3768d74f3d99f7ba137 Author: russ Date: Wed Mar 6 18:44:56 2019 -0500 stream_tcp: fixup allocation tracking for overlapped segmenets commit a9539d086d3956f2346b2bb04137b71b427464c6 Author: russ Date: Wed Mar 6 13:17:42 2019 -0500 memory: track session allocations commit e18575a5e608ea598b41175f10923b1061ea65ad Author: russ Date: Wed Mar 6 10:53:10 2019 -0500 memory: basic flow pruning commit 5da1c556989cd267c2718a4068b0e12edb7aea20 Author: russ Date: Wed Mar 6 10:52:38 2019 -0500 memory: refactor stats commit e6bfcd81fe52f018148b7c53ca3ce0520eadf532 Author: russ Date: Tue Mar 5 20:04:48 2019 -0500 memory: basic flow data allocation tracking commit 77f6ae93f8c5eb8f19df3b9d17736bb2655dcebc Author: russ Date: Tue Mar 5 10:20:02 2019 -0500 Revert "Merge pull request #1524 in SNORT/snort3 from ~PSHINDE2/snort3:memory_tracker_simplified to master" This reverts commit 0bb8323f6aae61501aaaaa6a9e904448ddf35ceb. Done to restore tracking of total allocations. Will fix differently. commit feb9b3707d1fc8c9b13fe236eb55433944704c7c Author: russ Date: Tue Mar 5 10:19:04 2019 -0500 memory: initial preemptive pruning based on flow data commit 118e0b21c8d2f4bc42d287bba7867d0ede1e728e Author: russ Date: Mon Mar 4 18:01:42 2019 -0500 memory: remove overloading manager to make way for new implementation --- src/detection/regex_offload.h | 2 +- src/file_api/file_flows.h | 3 + src/flow/flow.cc | 37 +- src/flow/flow.h | 9 + src/flow/flow_cache.cc | 19 +- src/flow/flow_cache.h | 1 + src/flow/flow_config.h | 1 + src/main/snort.cc | 2 + src/memory/CMakeLists.txt | 3 - src/memory/memory_allocator.cc | 39 -- src/memory/memory_allocator.h | 37 -- src/memory/memory_cap.cc | 76 ++- src/memory/memory_cap.h | 2 + src/memory/memory_config.h | 1 - src/memory/memory_manager.cc | 460 ------------------ src/memory/memory_module.cc | 28 +- src/memory/memory_module.h | 17 + src/network_inspectors/appid/appid_session.h | 7 +- src/service_inspectors/dce_rpc/dce_smb.h | 7 +- src/service_inspectors/dce_rpc/dce_tcp.h | 7 +- src/service_inspectors/dce_rpc/dce_udp.h | 7 +- src/service_inspectors/dnp3/dnp3.h | 7 +- src/service_inspectors/dns/dns.h | 3 + src/service_inspectors/ftp_telnet/ftpp_si.h | 9 + src/service_inspectors/gtp/gtp_inspect.h | 3 + .../http2_inspect/http2_flow_data.h | 3 + .../http_inspect/http_flow_data.h | 3 + src/service_inspectors/imap/imap.h | 3 + src/service_inspectors/modbus/modbus.h | 3 + src/service_inspectors/pop/pop.h | 3 + .../rpc_decode/rpc_decode.cc | 3 + src/service_inspectors/sip/sip.h | 3 + src/service_inspectors/smtp/smtp.h | 3 + src/service_inspectors/ssh/ssh.h | 3 + src/service_inspectors/ssl/ssl_inspector.h | 3 + src/stream/base/stream_base.cc | 1 + src/stream/base/stream_module.cc | 20 +- src/stream/file/file_session.cc | 6 +- src/stream/file/file_session.h | 1 + src/stream/icmp/icmp_session.cc | 7 +- src/stream/icmp/icmp_session.h | 1 + src/stream/ip/ip_defrag.cc | 3 + src/stream/ip/ip_session.cc | 7 +- src/stream/ip/ip_session.h | 1 + src/stream/libtcp/tcp_stream_session.cc | 9 - src/stream/libtcp/tcp_stream_session.h | 3 - src/stream/tcp/stream_tcp.cc | 16 +- src/stream/tcp/tcp_reassembler.cc | 8 +- src/stream/tcp/tcp_segment_node.cc | 97 +++- src/stream/tcp/tcp_segment_node.h | 22 +- src/stream/tcp/tcp_session.cc | 11 + src/stream/tcp/tcp_session.h | 3 + src/stream/udp/udp_session.cc | 8 +- src/stream/udp/udp_session.h | 1 + src/stream/user/user_session.cc | 14 +- src/stream/user/user_session.h | 2 + 56 files changed, 379 insertions(+), 679 deletions(-) delete mode 100644 src/memory/memory_allocator.cc delete mode 100644 src/memory/memory_allocator.h delete mode 100644 src/memory/memory_manager.cc diff --git a/src/detection/regex_offload.h b/src/detection/regex_offload.h index 0351f8ffb..fdc5faf8b 100644 --- a/src/detection/regex_offload.h +++ b/src/detection/regex_offload.h @@ -81,7 +81,7 @@ class ThreadRegexOffload : public RegexOffload { public: ThreadRegexOffload(unsigned max); - ~ThreadRegexOffload(); + ~ThreadRegexOffload() override; void stop() override; diff --git a/src/file_api/file_flows.h b/src/file_api/file_flows.h index f70a20977..91df59d58 100644 --- a/src/file_api/file_flows.h +++ b/src/file_api/file_flows.h @@ -92,6 +92,9 @@ public: void set_file_policy(FilePolicyBase* fp) { file_policy = fp; } FilePolicyBase* get_file_policy() { return file_policy; } + size_t size_of() override + { return sizeof(*this); } + private: void init_file_context(FileDirection, FileContext*); FileContext* find_main_file_context(FilePosition, FileDirection, size_t id = 0); diff --git a/src/flow/flow.cc b/src/flow/flow.cc index c30287cc6..1b28fd04a 100644 --- a/src/flow/flow.cc +++ b/src/flow/flow.cc @@ -28,6 +28,7 @@ #include "flow/session.h" #include "framework/data_bus.h" #include "ips_options/ips_flowbits.h" +#include "memory/memory_cap.h" #include "protocols/packet.h" #include "sfip/sf_ip.h" #include "utils/bitop.h" @@ -52,8 +53,27 @@ FlowData::~FlowData() { if ( handler ) handler->rem_ref(); + + assert(mem_in_use == 0); +} + +void FlowData::update_allocations(size_t n) +{ + memory::MemoryCap::free_space(n); + memory::MemoryCap::update_allocations(n); + mem_in_use += n; +} + +void FlowData::update_deallocations(size_t n) +{ + assert(mem_in_use >= n); + memory::MemoryCap::update_deallocations(n); + mem_in_use -= n; } +size_t FlowData::size_of() +{ return 1024; } // FIXIT-H remove this default impl + Flow::Flow() { memset(this, 0, sizeof(*this)); @@ -76,10 +96,13 @@ void Flow::init(PktType type) void Flow::term() { - if ( session ) - delete session; + if ( !session ) + return; - free_flow_data(); + delete session; + session = nullptr; + + assert(!flow_data); if ( mpls_client.length ) delete[] mpls_client.start; @@ -227,6 +250,12 @@ int Flow::set_flow_data(FlowData* fd) flow_data->prev = fd; flow_data = fd; + + // this is after actual allocation so we can't prune beforehand + // but if we are that close to the edge we are in trouble anyway + // large allocations can be accounted for directly + fd->update_allocations(fd->size_of()); + return 0; } @@ -262,6 +291,7 @@ void Flow::free_flow_data(FlowData* fd) fd->prev->next = fd->next; fd->next->prev = fd->prev; } + fd->update_deallocations(fd->size_of()); delete fd; } @@ -281,6 +311,7 @@ void Flow::free_flow_data() { FlowData* tmp = fd; fd = fd->next; + tmp->update_deallocations(tmp->size_of()); delete tmp; } flow_data = nullptr; diff --git a/src/flow/flow.h b/src/flow/flow.h index bd931507a..5e28e1f3d 100644 --- a/src/flow/flow.h +++ b/src/flow/flow.h @@ -115,6 +115,14 @@ public: static unsigned create_flow_data_id() { return ++flow_data_id; } + void update_allocations(size_t); + void update_deallocations(size_t); + + // return fixed size (could be an approx avg) + // this must be fixed for life of flow data instance + // track significant supplemental allocations with the above updaters + virtual size_t size_of() = 0; + virtual void handle_expected(Packet*) { } virtual void handle_retransmit(Packet*) { } virtual void handle_eof(Packet*) { } @@ -126,6 +134,7 @@ public: // FIXIT-L privatize private: static unsigned flow_data_id; Inspector* handler; + size_t mem_in_use = 0; unsigned id; }; diff --git a/src/flow/flow_cache.cc b/src/flow/flow_cache.cc index 0696259c1..d1c800303 100644 --- a/src/flow/flow_cache.cc +++ b/src/flow/flow_cache.cc @@ -28,6 +28,7 @@ #include "hash/zhash.h" #include "helpers/flag_context.h" #include "ips_options/ips_flowbits.h" +#include "memory/memory_cap.h" #include "packet_io/active.h" #include "time/packet_time.h" #include "utils/stats.h" @@ -61,9 +62,6 @@ FlowCache::FlowCache (const FlowConfig& cfg) : config(cfg) FlowCache::~FlowCache () { - while ( Flow* flow = (Flow*)hash_table->pop() ) - flow->term(); - delete uni_head; delete uni_tail; @@ -142,6 +140,7 @@ Flow* FlowCache::get(const FlowKey* key) link_uni(flow); } + memory::MemoryCap::update_allocations(config.cap_weight); flow->last_data_seen = timestamp; return flow; @@ -159,9 +158,18 @@ int FlowCache::remove(Flow* flow) if ( flow->next ) unlink_uni(flow); + memory::MemoryCap::update_deallocations(config.cap_weight); return hash_table->remove(flow->key); } +int FlowCache::retire(Flow* flow) +{ + flow->reset(true); + flow->term(); + prune_stats.update(PruneReason::NONE); + return remove(flow); +} + unsigned FlowCache::prune_stale(uint32_t thetime, const Flow* save_me) { ActiveSuspendContext act_susp; @@ -335,10 +343,13 @@ unsigned FlowCache::purge() while ( auto flow = static_cast(hash_table->first()) ) { - release(flow, PruneReason::NONE); + retire(flow); ++retired; } + while ( Flow* flow = (Flow*)hash_table->pop() ) + flow->term(); + return retired; } diff --git a/src/flow/flow_cache.h b/src/flow/flow_cache.h index a10ad9a8a..cff941974 100644 --- a/src/flow/flow_cache.h +++ b/src/flow/flow_cache.h @@ -79,6 +79,7 @@ public: private: void link_uni(snort::Flow*); int remove(snort::Flow*); + int retire(snort::Flow*); private: static const unsigned cleanup_flows = 1; diff --git a/src/flow/flow_config.h b/src/flow/flow_config.h index a1972db3e..95ab46bdb 100644 --- a/src/flow/flow_config.h +++ b/src/flow/flow_config.h @@ -28,6 +28,7 @@ struct FlowConfig unsigned max_sessions = 0; unsigned pruning_timeout = 0; unsigned nominal_timeout = 0; + unsigned cap_weight = 0; }; #endif diff --git a/src/main/snort.cc b/src/main/snort.cc index 141ab6570..2cb2acf6e 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -429,6 +429,8 @@ void Snort::term() already_exiting = true; initializing = false; // just in case we cut out early + memory::MemoryCap::print(); + term_signals(); IpsManager::global_term(SnortConfig::get_conf()); SFAT_Cleanup(); diff --git a/src/memory/CMakeLists.txt b/src/memory/CMakeLists.txt index e2de794f2..182861556 100644 --- a/src/memory/CMakeLists.txt +++ b/src/memory/CMakeLists.txt @@ -1,12 +1,9 @@ set ( MEMORY_SOURCES - memory_allocator.cc - memory_allocator.h memory_cap.cc memory_cap.h memory_module.cc memory_module.h memory_config.h - memory_manager.cc prune_handler.cc prune_handler.h ) diff --git a/src/memory/memory_allocator.cc b/src/memory/memory_allocator.cc deleted file mode 100644 index a91ec1ad8..000000000 --- a/src/memory/memory_allocator.cc +++ /dev/null @@ -1,39 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2016-2019 Cisco and/or its affiliates. All rights reserved. -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License Version 2 as published -// by the Free Software Foundation. You may not use, modify or distribute -// this program under any other version of the GNU General Public License. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -//-------------------------------------------------------------------------- - -// memory_allocator.cc author Joel Cornett - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "memory_allocator.h" - -#include - -namespace memory -{ - -// FIXIT-L (de)allocate() could be inlined if defined in memory_manager.cc -void* MemoryAllocator::allocate(size_t n) -{ return malloc(n); } - -void MemoryAllocator::deallocate(void* p) -{ free(p); } - -} // namespace memory diff --git a/src/memory/memory_allocator.h b/src/memory/memory_allocator.h deleted file mode 100644 index 71a867c16..000000000 --- a/src/memory/memory_allocator.h +++ /dev/null @@ -1,37 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2016-2019 Cisco and/or its affiliates. All rights reserved. -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License Version 2 as published -// by the Free Software Foundation. You may not use, modify or distribute -// this program under any other version of the GNU General Public License. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -//-------------------------------------------------------------------------- - -// memory_allocator.h author Joel Cornett - -#ifndef MEMORY_ALLOCATOR_H -#define MEMORY_ALLOCATOR_H - -#include - -namespace memory -{ - -struct MemoryAllocator -{ - static void* allocate(size_t); - static void deallocate(void*); -}; - -} // namespace memory - -#endif diff --git a/src/memory/memory_cap.cc b/src/memory/memory_cap.cc index bb00c15d2..524b26943 100644 --- a/src/memory/memory_cap.cc +++ b/src/memory/memory_cap.cc @@ -50,37 +50,21 @@ namespace struct Tracker { - size_t used_memory = 0; - - uint64_t allocations = 0; - uint64_t deallocations = 0; - void allocate(size_t n) - { - used_memory += n; - ++allocations; - } + { mem_stats.allocated += n; ++mem_stats.allocations; } void deallocate(size_t n) - { - // FIXIT-H If memory is allocated in one thread and passed to another thread, - // when it is freed, we may have (used_memory < memory_to_be_freed) - // Following assertion will fail. - //assert(n <= used_memory); - - if(n > used_memory) - return; - - used_memory -= n; - ++deallocations; - } + { mem_stats.deallocated += n; ++mem_stats.deallocations; } size_t used() const { - return used_memory; + if ( mem_stats.allocated < mem_stats.deallocated ) + { + assert(false); + return 0; + } + return mem_stats.allocated - mem_stats.deallocated; } - - constexpr Tracker() = default; }; static THREAD_LOCAL Tracker s_tracker; @@ -99,24 +83,23 @@ inline bool free_space(size_t requested, size_t cap, Tracker& trk, Handler& hand auto used = trk.used(); - if ( requested <= cap - used ) + if ( used + requested <= cap ) return true; - while ( requested > cap - used ) + ++mem_stats.reap_attempts; + + while ( used + requested > cap ) { handler(); - - // check if the handler freed any space auto tmp = trk.used(); + if ( tmp >= used ) { - // nope + ++mem_stats.reap_failures; return false; } - used = tmp; } - return true; } @@ -144,18 +127,27 @@ bool MemoryCap::free_space(size_t n) if ( !thread_cap ) return true; - const auto& config = *snort::SnortConfig::get_conf()->memory; - return memory::free_space(n, thread_cap, s_tracker, prune_handler) || config.soft; + return memory::free_space(n, thread_cap, s_tracker, prune_handler); } +static size_t fudge_it(size_t n) +{ return ((n >> 7) + 1) << 7; } + void MemoryCap::update_allocations(size_t n) { + size_t k = n; + n = fudge_it(n); + mem_stats.total_fudge += (n - k); s_tracker.allocate(n); + auto in_use = mem_stats.allocated - mem_stats.deallocated; + if ( in_use > mem_stats.max_in_use ) + mem_stats.max_in_use = in_use; mp_active_context.update_allocs(n); } void MemoryCap::update_deallocations(size_t n) { + n = fudge_it(n); s_tracker.deallocate(n); mp_active_context.update_deallocs(n); } @@ -214,21 +206,20 @@ void MemoryCap::print() const MemoryConfig& config = *snort::SnortConfig::get_conf()->memory; - if ( snort::SnortConfig::log_verbose() or s_tracker.allocations ) + if ( snort::SnortConfig::log_verbose() or mem_stats.allocations ) LogLabel("memory (heap)"); if ( snort::SnortConfig::log_verbose() ) { LogMessage(" global cap: %zu\n", config.cap); LogMessage(" global preemptive threshold percent: %u\n", config.threshold); - LogMessage(" cap type: %s\n", config.soft? "soft" : "hard"); } - if ( s_tracker.allocations ) + if ( mem_stats.allocations ) { LogMessage(" main thread usage: %zu\n", s_tracker.used()); - LogMessage(" allocations: %" PRIu64 "\n", s_tracker.allocations); - LogMessage(" deallocations: %" PRIu64 "\n", s_tracker.deallocations); + LogMessage(" allocations: %" PRIu64 "\n", mem_stats.allocations); + LogMessage(" deallocations: %" PRIu64 "\n", mem_stats.deallocations); LogMessage(" thread cap: %zu\n", thread_cap); LogMessage(" preemptive threshold: %zu\n", preemptive_threshold); } @@ -317,15 +308,6 @@ TEST_CASE( "memory cap free space", "[memory]" ) CHECK_FALSE( memory::free_space(1, 1024, tracker, handler) ); CHECK( handler.calls == 1 ); } - - SECTION( "free_space() does not rollover" ) - { - MockTracker tracker { SIZE_MAX }; - HandlerSpy handler { -5, tracker }; - CHECK( memory::free_space(1, SIZE_MAX, tracker, handler) ); - CHECK( handler.calls == 1 ); - } - } #endif diff --git a/src/memory/memory_cap.h b/src/memory/memory_cap.h index 80f5da1ea..58f3e4327 100644 --- a/src/memory/memory_cap.h +++ b/src/memory/memory_cap.h @@ -23,6 +23,8 @@ #include +#include "main/thread.h" + namespace memory { diff --git a/src/memory/memory_config.h b/src/memory/memory_config.h index 3e361191a..907752156 100644 --- a/src/memory/memory_config.h +++ b/src/memory/memory_config.h @@ -27,7 +27,6 @@ struct MemoryConfig { size_t cap = 0; unsigned threshold = 0; - bool soft = false; constexpr MemoryConfig() = default; }; diff --git a/src/memory/memory_manager.cc b/src/memory/memory_manager.cc deleted file mode 100644 index 339167a2a..000000000 --- a/src/memory/memory_manager.cc +++ /dev/null @@ -1,460 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2016-2019 Cisco and/or its affiliates. All rights reserved. -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License Version 2 as published -// by the Free Software Foundation. You may not use, modify or distribute -// this program under any other version of the GNU General Public License. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -//-------------------------------------------------------------------------- - -// memory_manager.cc author Joel Cornett - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "main/thread.h" - -#include "memory_allocator.h" -#include "memory_cap.h" - -#ifdef UNIT_TEST -#include "catch/snort_catch.h" -#endif - -namespace memory -{ - -// ----------------------------------------------------------------------------- -// metadata -// ----------------------------------------------------------------------------- - -// NOTE: This structure must be aligned to max_align_t as long as we are prepending -// it to memory allocations so that the returned memory is also aligned. -struct alignas(max_align_t) Metadata -{ -#if defined(REG_TEST) || defined(UNIT_TEST) - static constexpr size_t SANITY_CHECK_VALUE = 0xabcdef; - size_t sanity; -#endif - - // number of requested bytes - size_t payload_size; - - // total number of bytes allocated, including Metadata header - size_t total_size() const; - void* payload_offset(); - -#if defined(REG_TEST) || defined(UNIT_TEST) - bool valid() const - { return sanity == SANITY_CHECK_VALUE; } -#endif - - Metadata(size_t = 0); - - static size_t calculate_total_size(size_t); - - template - static Metadata* create(size_t); - - static Metadata* extract(void*); -}; - -inline size_t Metadata::total_size() const -{ return calculate_total_size(payload_size); } - -inline void* Metadata::payload_offset() -{ return this + 1; } - -inline Metadata::Metadata(size_t n) : -#if defined(REG_TEST) || defined(UNIT_TEST) - sanity(SANITY_CHECK_VALUE), -#endif - payload_size(n) -{ } - -inline size_t Metadata::calculate_total_size(size_t n) -{ return sizeof(Metadata) + n; } - -template -Metadata* Metadata::create(size_t n) -{ - auto meta = - static_cast(Allocator::allocate(calculate_total_size(n))); - - if ( !meta ) - return nullptr; - - // Trigger metadata ctor - *meta = Metadata(n); - -#if defined(REG_TEST) || defined(UNIT_TEST) - assert(meta->valid()); -#endif - - return meta; -} - -Metadata* Metadata::extract(void* p) -{ - assert(p); - - auto meta = static_cast(p) - 1; - -#if defined(REG_TEST) || defined(UNIT_TEST) - assert(meta->valid()); -#endif - - return meta; -} - -// ----------------------------------------------------------------------------- -// the meat -// ----------------------------------------------------------------------------- - -class ReentryContext -{ -public: - ReentryContext(bool& flag) : - already_entered(flag), flag(flag) - { flag = true; } - - ~ReentryContext() - { flag = false; } - - bool is_reentry() const - { return already_entered; } - -private: - const bool already_entered; - bool& flag; -}; - -template -struct Interface -{ - static void* allocate(size_t); - static void deallocate(void*); - - static THREAD_LOCAL bool in_allocation_call; -}; - -template -void* Interface::allocate(size_t n) -{ - // prevent allocation reentry - ReentryContext reentry_context(in_allocation_call); - assert(!reentry_context.is_reentry()); - - if ( !Cap::free_space(Metadata::calculate_total_size(n)) ) - return nullptr; - - auto meta = Metadata::create(n); - if ( !meta ) - return nullptr; - - Cap::update_allocations(meta->total_size()); - return meta->payload_offset(); -} - -template -void Interface::deallocate(void* p) -{ - if ( !p ) - return; - - auto meta = Metadata::extract(p); - assert(meta); - - Cap::update_deallocations(meta->total_size()); - Allocator::deallocate(meta); -} - -template -THREAD_LOCAL bool Interface::in_allocation_call = false; - -} //namespace memory - -// ----------------------------------------------------------------------------- -// new /delete replacements -// ----------------------------------------------------------------------------- - -// these don't have to be visible to operate as replacements - -#ifndef NO_MEM_MGR -void* operator new(size_t n) -{ - auto p = memory::Interface<>::allocate(n); - if ( !p ) - throw std::bad_alloc(); - - return p; -} - -void* operator new(size_t n, const std::nothrow_t&) noexcept -{ return memory::Interface<>::allocate(n); } - -void operator delete(void* p) noexcept -{ memory::Interface<>::deallocate(p); } - -void operator delete(void* p, const std::nothrow_t&) noexcept -{ ::operator delete(p); } - -void* operator new[](size_t n) -{ return ::operator new(n); } - -void* operator new[](size_t n, const std::nothrow_t& tag) noexcept -{ return ::operator new(n, tag); } - -void operator delete[](void* p) noexcept -{ ::operator delete(p); } - -void operator delete[](void* p, const std::nothrow_t&) noexcept -{ ::operator delete[](p); } - -// C++14 delete operators are a special case and must be explicitly exported -// since we're compiling as C++11 but must capture these for external libraries -void operator delete(void* p, size_t) noexcept; -SO_PUBLIC void operator delete(void* p, size_t) noexcept -{ ::operator delete(p); } - -void operator delete[](void* p, size_t) noexcept; -SO_PUBLIC void operator delete[](void* p, size_t) noexcept -{ ::operator delete[](p); } -#endif - -// ----------------------------------------------------------------------------- -// unit tests -// ----------------------------------------------------------------------------- - -#ifdef UNIT_TEST - -namespace t_memory -{ - -struct AllocatorSpy -{ - static void* allocate(size_t n) - { allocate_called = true; allocate_arg = n; return pool; } - - static void deallocate(void* p) - { deallocate_called = true; deallocate_arg = p; } - - static void reset() - { - pool = nullptr; - allocate_called = false; - allocate_arg = 0; - deallocate_called = false; - deallocate_arg = nullptr; - } - - static void* pool; - static bool allocate_called; - static size_t allocate_arg; - static bool deallocate_called; - static void* deallocate_arg; -}; - -void* AllocatorSpy::pool = nullptr; -bool AllocatorSpy::allocate_called = false; -size_t AllocatorSpy::allocate_arg = 0; -bool AllocatorSpy::deallocate_called = false; -void* AllocatorSpy::deallocate_arg = nullptr; - -struct CapSpy -{ - static bool free_space(size_t n) - { - free_space_called = true; - free_space_arg = n; - return free_space_result; - } - - static void update_allocations(size_t n) - { - update_allocations_called = true; - update_allocations_arg = n; - } - - static void update_deallocations(size_t n) - { - update_deallocations_called = true; - update_deallocations_arg = n; - } - - static void reset() - { - free_space_called = false; - free_space_arg = 0; - free_space_result = false; - - update_allocations_called = false; - update_allocations_arg = 0; - - update_deallocations_called = false; - update_deallocations_arg = 0; - } - - static bool free_space_called; - static size_t free_space_arg; - static bool free_space_result; - - static bool update_allocations_called; - static size_t update_allocations_arg; - - static bool update_deallocations_called; - static size_t update_deallocations_arg; -}; - -bool CapSpy::free_space_called = false; -size_t CapSpy::free_space_arg = 0; -bool CapSpy::free_space_result = false; - -bool CapSpy::update_allocations_called = false; -size_t CapSpy::update_allocations_arg = 0; - -bool CapSpy::update_deallocations_called = false; -size_t CapSpy::update_deallocations_arg = 0; - -} // namespace t_memory - -TEST_CASE( "memory metadata", "[memory]" ) -{ - using namespace t_memory; - - AllocatorSpy::reset(); - constexpr size_t n = 1; - char pool[sizeof(memory::Metadata) + n]; - - SECTION( "create" ) - { - AllocatorSpy::pool = pool; - - auto meta = memory::Metadata::create(n); - - CHECK( (void*)meta == (void*)pool ); - CHECK( meta->valid() ); - CHECK( meta->payload_size == n ); - } - - SECTION( "extract" ) - { - auto meta_pool = reinterpret_cast(pool); - meta_pool[0] = memory::Metadata(n); - - void* p = &meta_pool[1]; - - auto meta = memory::Metadata::extract(p); - - CHECK( (void*)meta == (void*)pool ); - CHECK( meta->payload_offset() == p ); - } -} - -TEST_CASE( "memory manager interface", "[memory]" ) -{ - using namespace t_memory; - - AllocatorSpy::reset(); - CapSpy::reset(); - - constexpr size_t n = 1; - char pool[sizeof(memory::Metadata) + n]; - - using Interface = memory::Interface; - - SECTION( "allocation" ) - { - SECTION( "free space failure" ) - { - auto p = Interface::allocate(n); - - CHECK( p == nullptr ); - - CHECK( CapSpy::free_space_called ); - CHECK( CapSpy::free_space_arg == memory::Metadata::calculate_total_size(n) ); - - CHECK_FALSE( AllocatorSpy::allocate_called ); - CHECK_FALSE( CapSpy::update_allocations_called ); - } - - SECTION( "allocation failure" ) - { - CapSpy::free_space_result = true; - - auto p = Interface::allocate(n); - - CHECK( p == nullptr ); - - CHECK( CapSpy::free_space_called ); - CHECK( CapSpy::free_space_arg == memory::Metadata::calculate_total_size(n) ); - - CHECK( AllocatorSpy::allocate_called ); - CHECK( AllocatorSpy::allocate_arg == memory::Metadata::calculate_total_size(n) ); - - CHECK_FALSE( CapSpy::update_allocations_called ); - } - - SECTION( "success" ) - { - CapSpy::free_space_result = true; - AllocatorSpy::pool = pool; - - auto p = Interface::allocate(n); - - CHECK( p > (void*)pool ); - - CHECK( CapSpy::free_space_called ); - CHECK( CapSpy::free_space_arg == memory::Metadata::calculate_total_size(n) ); - - CHECK( AllocatorSpy::allocate_called ); - CHECK( AllocatorSpy::allocate_arg == memory::Metadata::calculate_total_size(n) ); - - CHECK( CapSpy::update_allocations_called ); - CHECK( CapSpy::update_allocations_arg == memory::Metadata::calculate_total_size(n) ); - } - } - - SECTION( "deallocation" ) - { - SECTION( "nullptr" ) - { - Interface::deallocate(nullptr); - - CHECK_FALSE( AllocatorSpy::deallocate_called ); - CHECK_FALSE( CapSpy::update_deallocations_called ); - } - - SECTION( "success" ) - { - auto meta_pool = reinterpret_cast(pool); - meta_pool[0] = memory::Metadata(n); - - auto p = meta_pool[0].payload_offset(); - - Interface::deallocate(p); - - CHECK( AllocatorSpy::deallocate_called ); - CHECK( AllocatorSpy::deallocate_arg == (void*)pool ); - CHECK( CapSpy::update_deallocations_called ); - CHECK( CapSpy::update_deallocations_arg == memory::Metadata::calculate_total_size(n) ); - } - } - AllocatorSpy::pool = nullptr; - AllocatorSpy::deallocate_arg = nullptr; -} - -#endif diff --git a/src/memory/memory_module.cc b/src/memory/memory_module.cc index a0fe19863..9e255955b 100644 --- a/src/memory/memory_module.cc +++ b/src/memory/memory_module.cc @@ -43,9 +43,6 @@ static const Parameter s_params[] = { "cap", Parameter::PT_INT, "0:maxSZ", "0", "set the per-packet-thread cap on memory (bytes, 0 to disable)" }, - { "soft", Parameter::PT_BOOL, nullptr, "false", - "always succeed in allocating memory, even if above the cap" }, - { "threshold", Parameter::PT_INT, "0:100", "0", "set the per-packet-thread threshold for preemptive cleanup actions " "(percent, 0 to disable)" }, @@ -53,6 +50,22 @@ static const Parameter s_params[] = { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; +THREAD_LOCAL MemoryCounts mem_stats; +static MemoryCounts zero_stats = { }; + +const PegInfo mem_pegs[] = +{ + { CountType::SUM, "allocations", "total number of allocations" }, + { CountType::SUM, "deallocations", "total number of deallocations" }, + { CountType::SUM, "allocated", "total amount of memory allocated" }, + { CountType::SUM, "deallocated", "total amount of memory allocated" }, + { CountType::SUM, "reap_attempts", "attempts to reclaim memory" }, + { CountType::SUM, "reap_failures", "failures to reclaim memory" }, + { CountType::MAX, "max_in_use", "highest allocated - deallocated" }, + { CountType::SUM, "total_fudge", "sum of all adjustments" }, + { CountType::END, nullptr, nullptr } +}; + // ----------------------------------------------------------------------------- // memory module // ----------------------------------------------------------------------------- @@ -68,9 +81,6 @@ bool MemoryModule::set(const char*, Value& v, SnortConfig* sc) if ( v.is("cap") ) sc->memory->cap = v.get_size(); - else if ( v.is("soft") ) - sc->memory->soft = v.get_bool(); - else if ( v.is("threshold") ) sc->memory->threshold = v.get_uint8(); @@ -89,3 +99,9 @@ bool MemoryModule::end(const char*, int, SnortConfig*) bool MemoryModule::is_active() { return configured; } +const PegInfo* MemoryModule::get_pegs() const +{ return mem_pegs; } + +PegCount* MemoryModule::get_counts() const +{ return is_active() ? (PegCount*)&mem_stats : (PegCount*)&zero_stats; } + diff --git a/src/memory/memory_module.h b/src/memory/memory_module.h index 02179e200..11c17e83d 100644 --- a/src/memory/memory_module.h +++ b/src/memory/memory_module.h @@ -23,11 +23,28 @@ #include "framework/module.h" +struct MemoryCounts +{ + PegCount allocations; + PegCount deallocations; + PegCount allocated; + PegCount deallocated; + PegCount reap_attempts; + PegCount reap_failures; + PegCount max_in_use; + PegCount total_fudge; +}; + +extern THREAD_LOCAL MemoryCounts mem_stats; + class MemoryModule : public snort::Module { public: MemoryModule(); + const PegInfo* get_pegs() const override; + PegCount* get_counts() const override; + bool set(const char*, snort::Value&, snort::SnortConfig*) override; bool end(const char*, int, snort::SnortConfig*) override; diff --git a/src/network_inspectors/appid/appid_session.h b/src/network_inspectors/appid/appid_session.h index c90daa20d..3665f6fc4 100644 --- a/src/network_inspectors/appid/appid_session.h +++ b/src/network_inspectors/appid/appid_session.h @@ -195,9 +195,10 @@ public: uint16_t, IpProtocol, SnortProtocolId, int, AppIdInspector&); AppIdInspector& get_inspector() const - { - return inspector; - } + { return inspector; } + + size_t size_of() override + { return sizeof(*this); } uint32_t session_id = 0; snort::Flow* flow = nullptr; diff --git a/src/service_inspectors/dce_rpc/dce_smb.h b/src/service_inspectors/dce_rpc/dce_smb.h index 6fa3f1e23..d54977da3 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.h +++ b/src/service_inspectors/dce_rpc/dce_smb.h @@ -470,9 +470,10 @@ public: ~Dce2SmbFlowData() override; static void init() - { - inspector_id = snort::FlowData::create_flow_data_id(); - } + { inspector_id = snort::FlowData::create_flow_data_id(); } + + size_t size_of() override + { return sizeof(*this); } public: static unsigned inspector_id; diff --git a/src/service_inspectors/dce_rpc/dce_tcp.h b/src/service_inspectors/dce_rpc/dce_tcp.h index 4067d4f26..a3c9994a8 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp.h +++ b/src/service_inspectors/dce_rpc/dce_tcp.h @@ -119,9 +119,10 @@ public: ~Dce2TcpFlowData() override; static void init() - { - inspector_id = snort::FlowData::create_flow_data_id(); - } + { inspector_id = snort::FlowData::create_flow_data_id(); } + + size_t size_of() override + { return sizeof(*this); } public: static unsigned inspector_id; diff --git a/src/service_inspectors/dce_rpc/dce_udp.h b/src/service_inspectors/dce_rpc/dce_udp.h index 11e710363..9c388ac62 100644 --- a/src/service_inspectors/dce_rpc/dce_udp.h +++ b/src/service_inspectors/dce_rpc/dce_udp.h @@ -200,12 +200,13 @@ public: ~Dce2UdpFlowData() override; static void init() - { - inspector_id = snort::FlowData::create_flow_data_id(); - } + { inspector_id = snort::FlowData::create_flow_data_id(); } static unsigned inspector_id; DCE2_UdpSsnData dce2_udp_session; + + size_t size_of() override + { return sizeof(*this); } }; DCE2_UdpSsnData* get_dce2_udp_session_data(snort::Flow*); diff --git a/src/service_inspectors/dnp3/dnp3.h b/src/service_inspectors/dnp3/dnp3.h index 77ace13d0..b8a21089b 100644 --- a/src/service_inspectors/dnp3/dnp3.h +++ b/src/service_inspectors/dnp3/dnp3.h @@ -174,9 +174,10 @@ public: ~Dnp3FlowData() override; static void init() - { - inspector_id = snort::FlowData::create_flow_data_id(); - } + { inspector_id = snort::FlowData::create_flow_data_id(); } + + size_t size_of() override + { return sizeof(*this); } public: static unsigned inspector_id; diff --git a/src/service_inspectors/dns/dns.h b/src/service_inspectors/dns/dns.h index a201ee8fb..884dde9f7 100644 --- a/src/service_inspectors/dns/dns.h +++ b/src/service_inspectors/dns/dns.h @@ -175,6 +175,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; DNSData session; diff --git a/src/service_inspectors/ftp_telnet/ftpp_si.h b/src/service_inspectors/ftp_telnet/ftpp_si.h index ccf5a7af4..e8be8db6d 100644 --- a/src/service_inspectors/ftp_telnet/ftpp_si.h +++ b/src/service_inspectors/ftp_telnet/ftpp_si.h @@ -102,6 +102,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; TELNET_SESSION session; @@ -185,6 +188,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; FTP_SESSION session; @@ -222,6 +228,9 @@ public: void handle_expected(snort::Packet*) override; void handle_eof(snort::Packet*) override; + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; FTP_DATA_SESSION session; diff --git a/src/service_inspectors/gtp/gtp_inspect.h b/src/service_inspectors/gtp/gtp_inspect.h index 299d1c0c3..8b2ff19ce 100644 --- a/src/service_inspectors/gtp/gtp_inspect.h +++ b/src/service_inspectors/gtp/gtp_inspect.h @@ -43,6 +43,9 @@ public: static void init(); + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; GTP_Roptions ropts; diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index ebf897cea..d23bdbe99 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -41,6 +41,9 @@ public: friend bool implement_get_buf(unsigned id, Http2FlowData*, Http2Enums::SourceId, snort::InspectionBuffer&); + size_t size_of() override + { return sizeof(*this); } + protected: // 0 element refers to client frame, 1 element refers to server frame bool preface[2] = { true, false }; diff --git a/src/service_inspectors/http_inspect/http_flow_data.h b/src/service_inspectors/http_inspect/http_flow_data.h index 678fffc53..73ebac516 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.h +++ b/src/service_inspectors/http_inspect/http_flow_data.h @@ -63,6 +63,9 @@ public: friend class HttpUnitTestSetup; #endif + size_t size_of() override + { return sizeof(*this); } + private: // Convenience routines void half_reset(HttpEnums::SourceId source_id); diff --git a/src/service_inspectors/imap/imap.h b/src/service_inspectors/imap/imap.h index ec644b14c..0aa5ebd85 100644 --- a/src/service_inspectors/imap/imap.h +++ b/src/service_inspectors/imap/imap.h @@ -172,6 +172,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; IMAPData session; diff --git a/src/service_inspectors/modbus/modbus.h b/src/service_inspectors/modbus/modbus.h index b5ffd73c8..261644f99 100644 --- a/src/service_inspectors/modbus/modbus.h +++ b/src/service_inspectors/modbus/modbus.h @@ -54,6 +54,9 @@ public: ssn_data.flags = 0; } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; modbus_session_data_t ssn_data; diff --git a/src/service_inspectors/pop/pop.h b/src/service_inspectors/pop/pop.h index f9d420eb3..4e4bc2e03 100644 --- a/src/service_inspectors/pop/pop.h +++ b/src/service_inspectors/pop/pop.h @@ -126,6 +126,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; POPData session; diff --git a/src/service_inspectors/rpc_decode/rpc_decode.cc b/src/service_inspectors/rpc_decode/rpc_decode.cc index 0d30f17d6..37869c073 100644 --- a/src/service_inspectors/rpc_decode/rpc_decode.cc +++ b/src/service_inspectors/rpc_decode/rpc_decode.cc @@ -89,6 +89,9 @@ public: static void init() { inspector_id = FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; RpcSsnData session; diff --git a/src/service_inspectors/sip/sip.h b/src/service_inspectors/sip/sip.h index bcac9829a..74b4930db 100644 --- a/src/service_inspectors/sip/sip.h +++ b/src/service_inspectors/sip/sip.h @@ -45,6 +45,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; SIPData session; diff --git a/src/service_inspectors/smtp/smtp.h b/src/service_inspectors/smtp/smtp.h index 5ccf7bc3c..6a75a3cc5 100644 --- a/src/service_inspectors/smtp/smtp.h +++ b/src/service_inspectors/smtp/smtp.h @@ -171,6 +171,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; SMTPData session; diff --git a/src/service_inspectors/ssh/ssh.h b/src/service_inspectors/ssh/ssh.h index 6c1f03920..ad123b3b5 100644 --- a/src/service_inspectors/ssh/ssh.h +++ b/src/service_inspectors/ssh/ssh.h @@ -104,6 +104,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; SSHData session; diff --git a/src/service_inspectors/ssl/ssl_inspector.h b/src/service_inspectors/ssl/ssl_inspector.h index 63a812adf..f70ca34eb 100644 --- a/src/service_inspectors/ssl/ssl_inspector.h +++ b/src/service_inspectors/ssl/ssl_inspector.h @@ -45,6 +45,9 @@ public: static void init() { inspector_id = snort::FlowData::create_flow_data_id(); } + size_t size_of() override + { return sizeof(*this); } + public: static unsigned inspector_id; SSLData session; diff --git a/src/stream/base/stream_base.cc b/src/stream/base/stream_base.cc index 5797838f1..369048a95 100644 --- a/src/stream/base/stream_base.cc +++ b/src/stream/base/stream_base.cc @@ -304,6 +304,7 @@ static void base_dtor(Inspector* p) static void base_tterm() { + // this can't happen sooner because the counts haven't been harvested yet delete flow_con; flow_con = nullptr; } diff --git a/src/stream/base/stream_module.cc b/src/stream/base/stream_module.cc index 4a3f52866..c6aa2e058 100644 --- a/src/stream/base/stream_module.cc +++ b/src/stream/base/stream_module.cc @@ -37,7 +37,7 @@ using namespace std; //------------------------------------------------------------------------- Trace TRACE_NAME(stream); -#define CACHE_PARAMS(name, max, prune, idle, cleanup) \ +#define CACHE_PARAMS(name, max, prune, idle, weight) \ static const Parameter name[] = \ { \ { "max_sessions", Parameter::PT_INT, "2:max32", max, \ @@ -48,16 +48,19 @@ static const Parameter name[] = \ \ { "idle_timeout", Parameter::PT_INT, "1:max32", idle, \ "maximum inactive time before retiring session tracker" }, \ + \ + { "cap_weight", Parameter::PT_INT, "0:65535", idle, \ + "additional bytes to track per flow for better estimation against cap" }, \ \ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } \ } -CACHE_PARAMS(ip_params, "16384", "30", "180", "5"); -CACHE_PARAMS(icmp_params, "65536", "30", "180", "5"); -CACHE_PARAMS(tcp_params, "262144", "30", "3600", "5"); -CACHE_PARAMS(udp_params, "131072", "30", "180", "5"); -CACHE_PARAMS(user_params, "1024", "30", "180", "5"); -CACHE_PARAMS(file_params, "128", "30", "180", "5"); +CACHE_PARAMS(ip_params, "16384", "30", "180", "64"); +CACHE_PARAMS(icmp_params, "65536", "30", "180", "8"); +CACHE_PARAMS(tcp_params, "262144", "30", "3600", "256"); +CACHE_PARAMS(udp_params, "131072", "30", "180", "128"); +CACHE_PARAMS(user_params, "1024", "30", "180", "256"); +CACHE_PARAMS(file_params, "128", "30", "180", "32"); #define CACHE_TABLE(cache, proto, params) \ { cache, Parameter::PT_TABLE, params, nullptr, \ @@ -167,6 +170,9 @@ bool StreamModule::set(const char* fqn, Value& v, SnortConfig* c) else if ( v.is("idle_timeout") ) fc->nominal_timeout = v.get_uint32(); + else if ( v.is("cap_weight") ) + fc->cap_weight = v.get_uint16(); + else return false; diff --git a/src/stream/file/file_session.cc b/src/stream/file/file_session.cc index 464d1c377..d76e19b66 100644 --- a/src/stream/file/file_session.cc +++ b/src/stream/file/file_session.cc @@ -25,6 +25,7 @@ #include "detection/detection_engine.h" #include "file_api/file_flows.h" +#include "memory/memory_cap.h" #include "packet_io/sfdaq.h" #include "profiler/profiler_defs.h" #include "protocols/packet.h" @@ -42,8 +43,11 @@ static THREAD_LOCAL ProfileStats file_ssn_stats; // FileSession methods //------------------------------------------------------------------------- -FileSession::FileSession(Flow* flow) : Session(flow) { } +FileSession::FileSession(Flow* flow) : Session(flow) +{ memory::MemoryCap::update_allocations(sizeof(*this)); } +FileSession::~FileSession() +{ memory::MemoryCap::update_deallocations(sizeof(*this)); } bool FileSession::setup(Packet*) { diff --git a/src/stream/file/file_session.h b/src/stream/file/file_session.h index a3752970f..af443baa5 100644 --- a/src/stream/file/file_session.h +++ b/src/stream/file/file_session.h @@ -26,6 +26,7 @@ class FileSession : public Session { public: FileSession(snort::Flow*); + ~FileSession() override; bool setup(snort::Packet*) override; void clear() override; diff --git a/src/stream/icmp/icmp_session.cc b/src/stream/icmp/icmp_session.cc index 59f7716fa..230bc3457 100644 --- a/src/stream/icmp/icmp_session.cc +++ b/src/stream/icmp/icmp_session.cc @@ -24,6 +24,7 @@ #include "icmp_session.h" #include "flow/flow_key.h" +#include "memory/memory_cap.h" #include "profiler/profiler_defs.h" #include "protocols/icmp4.h" #include "protocols/packet.h" @@ -177,8 +178,10 @@ static int ProcessIcmpUnreach(Packet* p) //------------------------------------------------------------------------- IcmpSession::IcmpSession(Flow* flow) : Session(flow) -{ -} +{ memory::MemoryCap::update_allocations(sizeof(*this)); } + +IcmpSession::~IcmpSession() +{ memory::MemoryCap::update_deallocations(sizeof(*this)); } bool IcmpSession::setup(Packet*) { diff --git a/src/stream/icmp/icmp_session.h b/src/stream/icmp/icmp_session.h index afae36526..e0d35612f 100644 --- a/src/stream/icmp/icmp_session.h +++ b/src/stream/icmp/icmp_session.h @@ -26,6 +26,7 @@ class IcmpSession : public Session { public: IcmpSession(snort::Flow*); + ~IcmpSession() override; bool setup(snort::Packet*) override; void update_direction(char dir, const snort::SfIp*, uint16_t port) override; diff --git a/src/stream/ip/ip_defrag.cc b/src/stream/ip/ip_defrag.cc index b6a102981..15bd874aa 100644 --- a/src/stream/ip/ip_defrag.cc +++ b/src/stream/ip/ip_defrag.cc @@ -75,6 +75,7 @@ #include "log/messages.h" #include "main/snort.h" #include "main/snort_config.h" +#include "memory/memory_cap.h" #include "packet_io/active.h" #include "packet_io/sfdaq.h" #include "profiler/profiler_defs.h" @@ -135,6 +136,7 @@ struct Fragment { delete[] fptr; ip_stats.nodes_released++; + memory::MemoryCap::update_deallocations(sizeof(*this) + flen); } uint8_t* data = nullptr; /* ptr to adjusted start position */ @@ -154,6 +156,7 @@ private: inline void init(uint16_t flen, const uint8_t* fptr, int ord) { assert(flen > 0); + memory::MemoryCap::update_allocations(sizeof(*this) + flen); this->flen = flen; this->fptr = new uint8_t[flen]; diff --git a/src/stream/ip/ip_session.cc b/src/stream/ip/ip_session.cc index 2dd396656..0a79c34b2 100644 --- a/src/stream/ip/ip_session.cc +++ b/src/stream/ip/ip_session.cc @@ -23,6 +23,7 @@ #include "ip_session.h" +#include "memory/memory_cap.h" #include "profiler/profiler_defs.h" #include "protocols/packet.h" @@ -115,8 +116,10 @@ static inline void UpdateSession(Packet* p, Flow* lws) //------------------------------------------------------------------------- IpSession::IpSession(Flow* flow) : Session(flow) -{ -} +{ memory::MemoryCap::update_allocations(sizeof(*this)); } + +IpSession::~IpSession() +{ memory::MemoryCap::update_deallocations(sizeof(*this)); } void IpSession::clear() { diff --git a/src/stream/ip/ip_session.h b/src/stream/ip/ip_session.h index ccedb73d4..0a93354c8 100644 --- a/src/stream/ip/ip_session.h +++ b/src/stream/ip/ip_session.h @@ -76,6 +76,7 @@ class IpSession : public Session { public: IpSession(snort::Flow*); + ~IpSession() override; bool setup(snort::Packet*) override; int process(snort::Packet*) override; diff --git a/src/stream/libtcp/tcp_stream_session.cc b/src/stream/libtcp/tcp_stream_session.cc index f2a710069..d8e131678 100644 --- a/src/stream/libtcp/tcp_stream_session.cc +++ b/src/stream/libtcp/tcp_stream_session.cc @@ -463,14 +463,6 @@ void TcpStreamSession::start_proxy() // tcp module stuff //------------------------------------------------------------------------- -void TcpStreamSession::sinit() -{ - //AtomSplitter::init(); // FIXIT-L PAF implement -} - -void TcpStreamSession::sterm() -{ } - void TcpStreamSession::print() { char buf[64]; @@ -489,4 +481,3 @@ void TcpStreamSession::print() server.print(); } - diff --git a/src/stream/libtcp/tcp_stream_session.h b/src/stream/libtcp/tcp_stream_session.h index 2bfe6319e..27d08672d 100644 --- a/src/stream/libtcp/tcp_stream_session.h +++ b/src/stream/libtcp/tcp_stream_session.h @@ -58,9 +58,6 @@ public: uint16_t get_mss(bool to_server) const; uint8_t get_tcp_options_len(bool to_server) const; - static void sinit(); - static void sterm(); - void reset(); void start_proxy(); void print(); diff --git a/src/stream/tcp/stream_tcp.cc b/src/stream/tcp/stream_tcp.cc index 4f473c31c..b973ab7e8 100644 --- a/src/stream/tcp/stream_tcp.cc +++ b/src/stream/tcp/stream_tcp.cc @@ -76,11 +76,13 @@ bool StreamTcp::configure(SnortConfig* sc) void StreamTcp::tinit() { TcpHAManager::tinit(); + TcpSession::sinit(); } void StreamTcp::tterm() { TcpHAManager::tterm(); + TcpSession::sterm(); } NORETURN_ASSERT void StreamTcp::eval(Packet*) @@ -121,16 +123,6 @@ static Session* tcp_ssn(Flow* lws) return new TcpSession(lws); } -static void tcp_tinit() -{ - TcpSession::sinit(); -} - -static void tcp_tterm() -{ - TcpSession::sterm(); -} - static const InspectApi tcp_api = { { @@ -151,8 +143,8 @@ static const InspectApi tcp_api = nullptr, // service nullptr, // init nullptr, // term - tcp_tinit, - tcp_tterm, + nullptr, // tinit, + nullptr, // tterm, tcp_ctor, tcp_dtor, tcp_ssn, diff --git a/src/stream/tcp/tcp_reassembler.cc b/src/stream/tcp/tcp_reassembler.cc index b538fa2b9..7cf2510ac 100644 --- a/src/stream/tcp/tcp_reassembler.cc +++ b/src/stream/tcp/tcp_reassembler.cc @@ -28,6 +28,7 @@ #include "detection/detection_engine.h" #include "log/log.h" #include "main/snort.h" +#include "memory/memory_cap.h" #include "profiler/profiler.h" #include "protocols/packet_manager.h" #include "time/packet_time.h" @@ -218,6 +219,7 @@ int TcpReassembler::add_reassembly_segment( // FIXIT-L don't allocate overlapped part tsn = TcpSegmentNode::init(tsd); + tsn->offset = slide; tsn->c_len = (uint16_t)newSize; tsn->i_len = (uint16_t)newSize; @@ -1075,8 +1077,10 @@ int TcpReassembler::flush_on_data_policy(TcpReassemblerState& trs, Packet* p) } break; } - - if ( flushed and !trs.sos.session->flow->two_way_traffic() and !p->ptrs.tcph->is_syn() ) + // FIXIT-H a drop rule will yoink the seglist out from under us + // because apply_delayed_action is only deferred to end of context + if ( flushed and trs.sos.seg_count and + !trs.sos.session->flow->two_way_traffic() and !p->ptrs.tcph->is_syn() ) { TcpStreamTracker::TcpState peer = trs.tracker->session->get_peer_state(trs.tracker); diff --git a/src/stream/tcp/tcp_segment_node.cc b/src/stream/tcp/tcp_segment_node.cc index 1ac521545..8f613919c 100644 --- a/src/stream/tcp/tcp_segment_node.cc +++ b/src/stream/tcp/tcp_segment_node.cc @@ -25,42 +25,115 @@ #include "tcp_segment_node.h" +#include "main/thread.h" +#include "memory/memory_cap.h" #include "utils/util.h" #include "segment_overlap_editor.h" #include "tcp_module.h" -TcpSegmentNode::TcpSegmentNode(const struct timeval& tv, const uint8_t* payload, uint16_t len) : - prev(nullptr), next(nullptr), tv(tv), ts(0), i_seq(0), c_seq(0), i_len(len), - c_len(len), offset(0) +#define USE_RESERVE +#ifdef USE_RESERVE +static THREAD_LOCAL TcpSegmentNode* reserved = nullptr; +static THREAD_LOCAL unsigned reserve_sz = 0; + +static constexpr unsigned num_res = 4096; +static constexpr unsigned res_min = 1024; +static constexpr unsigned res_max = 1460; +#endif + +void TcpSegmentNode::setup() +{ +#ifdef USE_RESERVE + reserved = nullptr; + reserve_sz = 0; +#endif +} + +void TcpSegmentNode::clear() { - data = ( uint8_t* )snort_alloc(len); - memcpy(data, payload, len); - tcpStats.mem_in_use += len; +#ifdef USE_RESERVE + while ( reserved ) + { + TcpSegmentNode* tsn = reserved; + reserved = reserved->next; + memory::MemoryCap::update_deallocations(sizeof(*tsn) + tsn->size); + tcpStats.mem_in_use -= tsn->size; + snort_free(tsn); + } + reserve_sz = 0; +#endif } //------------------------------------------------------------------------- // TcpSegment stuff //------------------------------------------------------------------------- + +TcpSegmentNode* TcpSegmentNode::create( + const struct timeval& tv, const uint8_t* payload, uint16_t len) +{ + TcpSegmentNode* tsn; + +#ifdef USE_RESERVE + if ( reserved and len > res_min and len <= res_max ) + { + tsn = reserved; + reserved = tsn->next; + --reserve_sz; + } + else +#endif + { + size_t size = sizeof(*tsn) + len; + memory::MemoryCap::update_allocations(size); + tsn = (TcpSegmentNode*)snort_alloc(size); + tsn->size = len; + tcpStats.mem_in_use += len; + } + tsn->tv = tv; + tsn->i_len = tsn->c_len = len; + memcpy(tsn->data, payload, len); + + tsn->prev = tsn->next = nullptr; + tsn->i_seq = tsn->c_seq = 0; + tsn->offset = 0; + tsn->ts = 0; + + return tsn; +} + TcpSegmentNode* TcpSegmentNode::init(TcpSegmentDescriptor& tsd) { - return new TcpSegmentNode(tsd.get_pkt()->pkth->ts, tsd.get_pkt()->data, tsd.get_seg_len()); + return create(tsd.get_pkt()->pkth->ts, tsd.get_pkt()->data, tsd.get_seg_len()); } TcpSegmentNode* TcpSegmentNode::init(TcpSegmentNode& tns) { - return new TcpSegmentNode(tns.tv, tns.payload(), tns.c_len); + return create(tns.tv, tns.payload(), tns.c_len); } void TcpSegmentNode::term() { - snort_free(data); +#ifdef USE_RESERVE + if ( size == res_max and reserve_sz < num_res ) + { + next = reserved; + reserved = this; + reserve_sz++; + } + else +#endif + { + memory::MemoryCap::update_deallocations(sizeof(*this) + size); + tcpStats.mem_in_use -= size; + snort_free(this); + } tcpStats.segs_released++; - tcpStats.mem_in_use -= i_len; - delete this; } -bool TcpSegmentNode::is_retransmit(const uint8_t* rdata, uint16_t rsize, uint32_t rseq, uint16_t orig_dsize, bool *full_retransmit) +bool TcpSegmentNode::is_retransmit( + const uint8_t* rdata, uint16_t rsize, uint32_t rseq, uint16_t orig_dsize, + bool *full_retransmit) { // retransmit must have same payload at same place if ( !SEQ_EQ(i_seq, rseq) ) diff --git a/src/stream/tcp/tcp_segment_node.h b/src/stream/tcp/tcp_segment_node.h index 5be3265b6..885548d20 100644 --- a/src/stream/tcp/tcp_segment_node.h +++ b/src/stream/tcp/tcp_segment_node.h @@ -32,19 +32,25 @@ class TcpSegmentDescriptor; // we make a lot of TcpSegments so it is organized by member // size/alignment requirements to minimize unused space // ... however, use of padding below is critical, adjust if needed +// and we use the struct hack to avoid 2 allocs per node //----------------------------------------------------------------- class TcpSegmentNode { -public: - TcpSegmentNode(const struct timeval& tv, const uint8_t* segment, uint16_t len); +private: + static TcpSegmentNode* create(const struct timeval& tv, const uint8_t* segment, uint16_t len); - static TcpSegmentNode* init(TcpSegmentDescriptor& tsd); - static TcpSegmentNode* init(TcpSegmentNode& tns); - static TcpSegmentNode* init(const struct timeval&, const uint8_t*, unsigned); +public: + static TcpSegmentNode* init(TcpSegmentDescriptor&); + static TcpSegmentNode* init(TcpSegmentNode&); void term(); + + static void setup(); + static void clear(); + bool is_retransmit(const uint8_t*, uint16_t size, uint32_t, uint16_t, bool*); + uint8_t* payload() { return data + offset; } @@ -56,11 +62,10 @@ public: return (c_seq + c_len) < to_seq; } +public: TcpSegmentNode* prev; TcpSegmentNode* next; - uint8_t* data; - struct timeval tv; uint32_t ts; uint32_t i_seq; // initial seq # of the data segment @@ -68,6 +73,9 @@ public: uint16_t i_len; // initial length of the data segment uint16_t c_len; // length of data remaining for reassembly uint16_t offset; + uint16_t size; // actual allocated size (overlaps cause i_len to differ) + + uint8_t data[1]; }; class TcpSegmentList diff --git a/src/stream/tcp/tcp_session.cc b/src/stream/tcp/tcp_session.cc index 57813b6d7..f5aa03aa9 100644 --- a/src/stream/tcp/tcp_session.cc +++ b/src/stream/tcp/tcp_session.cc @@ -51,6 +51,7 @@ #include "detection/detection_engine.h" #include "detection/rules.h" #include "log/log.h" +#include "memory/memory_cap.h" #include "profiler/profiler.h" #include "protocols/eth.h" @@ -60,10 +61,17 @@ #include "tcp_module.h" #include "tcp_normalizers.h" #include "tcp_reassemblers.h" +#include "tcp_segment_node.h" #include "tcp_stream_state_machine.h" using namespace snort; +void TcpSession::sinit() +{ TcpSegmentNode::setup(); } + +void TcpSession::sterm() +{ TcpSegmentNode::clear(); } + TcpSession::TcpSession(Flow* flow) : TcpStreamSession(flow) { @@ -73,11 +81,14 @@ TcpSession::TcpSession(Flow* flow) client.session = this; server.session = this; tcpStats.instantiated++; + + memory::MemoryCap::update_allocations(sizeof(*this)); } TcpSession::~TcpSession() { clear_session(true, false, false); + memory::MemoryCap::update_deallocations(sizeof(*this)); } bool TcpSession::setup(Packet* p) diff --git a/src/stream/tcp/tcp_session.h b/src/stream/tcp/tcp_session.h index 8183f6191..7b9de1e5b 100644 --- a/src/stream/tcp/tcp_session.h +++ b/src/stream/tcp/tcp_session.h @@ -38,6 +38,9 @@ public: TcpSession(snort::Flow*); ~TcpSession() override; + static void sinit(); + static void sterm(); + bool setup(snort::Packet*) override; void restart(snort::Packet* p) override; void precheck(snort::Packet* p) override; diff --git a/src/stream/udp/udp_session.cc b/src/stream/udp/udp_session.cc index 3beb3d9e4..f7f33a3f7 100644 --- a/src/stream/udp/udp_session.cc +++ b/src/stream/udp/udp_session.cc @@ -26,6 +26,7 @@ #include "flow/session.h" #include "framework/data_bus.h" #include "hash/xhash.h" +#include "memory/memory_cap.h" #include "profiler/profiler_defs.h" #include "protocols/packet.h" @@ -102,8 +103,10 @@ static int ProcessUdp( //------------------------------------------------------------------------- UdpSession::UdpSession(Flow* flow) : Session(flow) -{ -} +{ memory::MemoryCap::update_allocations(sizeof(*this)); } + +UdpSession::~UdpSession() +{ memory::MemoryCap::update_deallocations(sizeof(*this)); } bool UdpSession::setup(Packet* p) { @@ -135,7 +138,6 @@ void UdpSession::clear() { UdpSessionCleanup(flow); UdpHAManager::process_deletion(flow); - flow->clear(); } void UdpSession::update_direction( diff --git a/src/stream/udp/udp_session.h b/src/stream/udp/udp_session.h index 5d67010f4..860a4532d 100644 --- a/src/stream/udp/udp_session.h +++ b/src/stream/udp/udp_session.h @@ -28,6 +28,7 @@ class UdpSession : public Session { public: UdpSession(snort::Flow*); + ~UdpSession() override; bool setup(snort::Packet*) override; void update_direction(char dir, const snort::SfIp*, uint16_t port) override; diff --git a/src/stream/user/user_session.cc b/src/stream/user/user_session.cc index 8a6a62e88..e510791af 100644 --- a/src/stream/user/user_session.cc +++ b/src/stream/user/user_session.cc @@ -26,6 +26,7 @@ #include "detection/detection_engine.h" #include "detection/rules.h" #include "main/snort.h" +#include "memory/memory_cap.h" #include "profiler/profiler_defs.h" #include "protocols/packet.h" #include "utils/util.h" @@ -53,8 +54,12 @@ THREAD_LOCAL ProfileStats user_perf_stats; UserSegment* UserSegment::init(const uint8_t* p, unsigned n) { unsigned bucket = (n > BUCKET) ? n : BUCKET; - UserSegment* us = (UserSegment*)snort_alloc(sizeof(*us)+bucket-1); + unsigned size = sizeof(UserSegment) + bucket -1; + memory::MemoryCap::update_allocations(size); + UserSegment* us = (UserSegment*)snort_alloc(size); + + us->size = size; us->len = 0; us->offset = 0; us->used = 0; @@ -65,6 +70,7 @@ UserSegment* UserSegment::init(const uint8_t* p, unsigned n) void UserSegment::term(UserSegment* us) { + memory::MemoryCap::update_deallocations(us->size); snort_free(us); } @@ -424,8 +430,11 @@ void UserSession::restart(Packet* p) // UserSession methods //------------------------------------------------------------------------- -UserSession::UserSession(Flow* flow) : Session(flow) { } +UserSession::UserSession(Flow* flow) : Session(flow) +{ memory::MemoryCap::update_allocations(sizeof(*this)); } +UserSession::~UserSession() +{ memory::MemoryCap::update_deallocations(sizeof(*this)); } bool UserSession::setup(Packet*) { @@ -443,7 +452,6 @@ void UserSession::clear() { client.term(); server.term(); - flow->restart(); } void UserSession::set_splitter(bool c2s, StreamSplitter* ss) diff --git a/src/stream/user/user_session.h b/src/stream/user/user_session.h index 73183016a..2f263f255 100644 --- a/src/stream/user/user_session.h +++ b/src/stream/user/user_session.h @@ -48,6 +48,7 @@ private: unsigned len; unsigned offset; unsigned used; + unsigned size; uint8_t data[1]; }; @@ -75,6 +76,7 @@ class UserSession : public Session { public: UserSession(snort::Flow*); + ~UserSession() override; bool setup(snort::Packet*) override; void clear() override; -- 2.47.3