Project

General

Profile

Actions

Bug #9342

open
JL JL

stream: pcap header memory counted twice in reassembly memuse

Bug #9342: stream: pcap header memory counted twice in reassembly memuse

Added by Jeff Lucovsky 2 days ago. Updated 1 day ago.

Status:
In Review
Priority:
Normal
Assignee:
Target version:
Affected Versions:
Effort:
Difficulty:
Label:

Description

When pcap-log runs with `conditional: alerts` or `conditional: tag`, Suricata keeps a small pcap header next to every TCP segment. The memory for that header gets added to the stream reassembly memory counter twice, but it's only subtracted once when the segment is freed. Any config like this triggers it:

outputs:
  - pcap-log:
      enabled: yes
      conditional: alerts

Each segment freed by the segment pool leaves 88 bytes behind in the counter (24 bytes for the header struct plus a 64 byte buffer). The memory itself is freed fine, so you won't see it in RSS. But the counter keeps creeping up as the pool frees segments, and since the reassembly memcap check uses that counter, Suricata can eventually start refusing new segments (tcp.segment_memcap_drop goes up) even though real usage is below stream.reassembly.memcap.

The two places that add it are TcpSegmentPoolAlloc() and TcpSegmentPoolInit(). TcpSegmentPoolCleanup() subtracts it once. The extra add in Alloc() came in with 2bf3172dd ("stream: memcap tracking for TcpSegment alloc"), first released in 7.0.0, so main and 8.0.x are both affected.

There's also a leak on the same path. Alloc() allocates the header, but when Init() fails its memcap check, the pool frees the segment without calling Cleanup(), so the header is never freed. Because Alloc() has already added its 88 bytes, Init() fails whenever usage is within 88 bytes of the memcap, so on main this happens near the memcap even with one thread. Each failure leaks 88 bytes and leaves another 88 in the counter.

Proposed fix: drop the add in TcpSegmentPoolAlloc() so Init() and Cleanup() pair up, the same way sizeof(TcpSegment) is already handled, and free the header in Init() before it returns failure. After that, Init() can only fail if another thread raises usage or the memcap is lowered between the two calls. Two unit tests cover this: one checks that an Alloc()/Init()/Cleanup() cycle leaves the counter unchanged, and the other forces Init() to fail and checks the header is freed. A suricata-verify test reuses the nfs3-lookup-overclaim-skip pcap with segment-prealloc set to 0, so each of the 725 segments the pool allocates is freed through it. On x86_64 the final tcp.reassembly_memuse is 76040 bytes before the fix and 12240 after.


Subtasks 1 (1 open — 0 closed)

Bug #9343: stream: pcap header memory counted twice in reassembly memuse (8.0.x backport)AssignedJeff LucovskyActions

JL Updated by Jeff Lucovsky 1 day ago Actions #1

  • Description updated (diff)

JL Updated by Jeff Lucovsky 1 day ago Actions #2

  • Status changed from New to In Review

JL Updated by Jeff Lucovsky 1 day ago Actions #3

  • Target version changed from TBD to 9.0.0-beta1
  • Label Needs backport to 8.0 added

OT Updated by OISF Ticketbot 1 day ago Actions #4

  • Subtask #9343 added

OT Updated by OISF Ticketbot 1 day ago Actions #5

  • Label deleted (Needs backport to 8.0)
Actions

Also available in: PDF Atom