From 6f29f75e683514151ed5c13f9fb63d8b77c0a944 Mon Sep 17 00:00:00 2001 From: Christian Aurich Date: Sat, 5 Sep 2026 15:19:01 -0300 Subject: [PATCH] quic: reject zero addressLRUSize SocketAddressLRU::Upsert always inserts an entry before evicting down to max_size_. With max_size_ == 0, it evicts the entry it just inserted and then accesses the now-missing key via map_[address]->second. operator[] recreates the key with a default-constructed std::list iterator, which is then dereferenced. This is undefined behavior, observed as a SIGSEGV in Endpoint::Receive on the first UDP packet accepted by a QuicEndpoint constructed with { addressLRUSize: 0 }. SocketAddressLRU has no useful semantics for a zero-capacity cache, and Upsert's callers rely on it returning a valid pointer. Reject 0 (and 0n) at the options-parsing boundary instead of changing Upsert's contract. Signed-off-by: Christian Aurich --- src/quic/endpoint.cc | 11 +++++++++++ test/parallel/test-quic-internal-endpoint-options.mjs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/quic/endpoint.cc b/src/quic/endpoint.cc index a6a4109509ae..8a92986c5a9c 100644 --- a/src/quic/endpoint.cc +++ b/src/quic/endpoint.cc @@ -249,6 +249,17 @@ Maybe Endpoint::Options::From(Environment* env, return Nothing(); } + // SocketAddressLRU::Upsert requires a positive capacity. With max_size_ == + // 0, the newly inserted entry is immediately evicted, and the final + // map_[address] creates a default list iterator that is then + // dereferenced, causing UB (observed as a SIGSEGV in Endpoint::Receive on + // the first accepted connection). + if (options.address_lru_size == 0) { + THROW_ERR_INVALID_ARG_VALUE( + env, "The addressLRUSize option must be greater than 0"); + return Nothing(); + } + Local address; if (!params->Get(env->context(), env->address_string()).ToLocal(&address)) { return Nothing(); diff --git a/test/parallel/test-quic-internal-endpoint-options.mjs b/test/parallel/test-quic-internal-endpoint-options.mjs index 29529c05e613..a81832c71094 100644 --- a/test/parallel/test-quic-internal-endpoint-options.mjs +++ b/test/parallel/test-quic-internal-endpoint-options.mjs @@ -56,7 +56,7 @@ const cases = [ valid: [ 1, 10, 100, 1000, 10000, 10000n, ], - invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + invalid: [-1, -1n, 0, 0n, 'a', null, false, true, {}, [], () => {}] }, { key: 'retryRate',