[SECURITY][high] Per-connection memory budget bypassed by decompressed/copied chunk buffers -> multi-GB OOM in multithreaded receiver #254

Closed
opened 2026-09-05 12:08:20 +02:00 by TapTap · 1 comment
Owner

Found by security audit of dev (9f8b588).

Where: src/shared/chunk.c:310-338 (receive_chunk_data decompress), chunk.c:247-255 (uncharged per-file memcpy copy), src/shared/protocol.c:90-104,446-484 (reserve only covers wire buffer), src/server/server.c:298 + src/shared/multiprocessing.c:99-135 (queue depth 100).

Problem: protocol_reserve_memory()/MAX_CONNECTION_MEMORY (256MB) only accounts for buffers from receive_data_limited(). Decompression buffers and per-file chunk_deserialize copies are NOT charged to total_allocated_bytes; only per-allocation max_alloc gates them. Multithreaded receiver enqueues up to 100 files ahead of the disk writer, each up to 64MB uncompressed. Since chunks arrive compressed, an attacker (any --allow-unauthenticated/stdio/authenticated peer) feeds the queue with little bandwidth -> heap high-water ~100*64MB = ~6.4GB per connection, up to 100 connections -> OOM host.

Fix: Charge per-file payload allocations and decompression buffers against the session memory reserve, or bound the receiver queue by bytes.

Verify with a targeted test where possible.

Found by security audit of `dev` (9f8b588). **Where:** src/shared/chunk.c:310-338 (receive_chunk_data decompress), chunk.c:247-255 (uncharged per-file memcpy copy), src/shared/protocol.c:90-104,446-484 (reserve only covers wire buffer), src/server/server.c:298 + src/shared/multiprocessing.c:99-135 (queue depth 100). **Problem:** `protocol_reserve_memory()`/MAX_CONNECTION_MEMORY (256MB) only accounts for buffers from receive_data_limited(). Decompression buffers and per-file chunk_deserialize copies are NOT charged to total_allocated_bytes; only per-allocation max_alloc gates them. Multithreaded receiver enqueues up to 100 files ahead of the disk writer, each up to 64MB uncompressed. Since chunks arrive *compressed*, an attacker (any --allow-unauthenticated/stdio/authenticated peer) feeds the queue with little bandwidth -> heap high-water ~100*64MB = ~6.4GB per connection, up to 100 connections -> OOM host. **Fix:** Charge per-file payload allocations and decompression buffers against the session memory reserve, or bound the receiver queue by bytes. Verify with a targeted test where possible.
Author
Owner

Resolved on dev (HEAD f7c6c91).

Fixed on dev via fix/security-hardening (98120fc): receiver queue bounded by aggregate payload bytes (RECEIVER_QUEUE_MAX_BYTES) with blocking backpressure; writer releases budget per file. Unit test test_receiver_enqueue_byte_budget added. Reconciled with outcome reporting at merge (68e7ed5).

CI run #461: all jobs green (lint, build-and-test, sanitizers address+undefined, fuzz-build, coverage, valgrind). Unit 25/25, integration 83 passed / 10 skipped / 1 xpassed. Closing.

**Resolved on `dev`** (HEAD f7c6c91). Fixed on dev via fix/security-hardening (98120fc): receiver queue bounded by aggregate payload bytes (RECEIVER_QUEUE_MAX_BYTES) with blocking backpressure; writer releases budget per file. Unit test test_receiver_enqueue_byte_budget added. Reconciled with outcome reporting at merge (68e7ed5). CI run #461: all jobs green (lint, build-and-test, sanitizers address+undefined, fuzz-build, coverage, valgrind). Unit 25/25, integration 83 passed / 10 skipped / 1 xpassed. Closing.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: TapTap/FastSync#254