Skip to content

fix(config): bound message size, watchdog, and ring size at load time#826

Merged
userFRM merged 1 commit into
mainfrom
fix/config-bounds-hardening
Jun 16, 2026
Merged

fix(config): bound message size, watchdog, and ring size at load time#826
userFRM merged 1 commit into
mainfrom
fix/config-bounds-hardening

Conversation

@userFRM

@userFRM userFRM commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Three configuration knobs accepted out-of-range values that the loader then propagated into the engine as oversized memory commitments or arithmetic that wrapped under the operator's stated tuning. Each is now bounded at load/validate time and reported by name so a misconfiguration fails fast instead of running a silently-corrupted budget.

[grpc] max_message_size_mb

The MB override was multiplied by 1024 * 1024 straight from TOML with no range check. An absurd value overflowed the byte conversion (debug panic / release wrap into a tiny garbage cap) and a 0 silently uncapped the inbound decode budget. The override is now rejected unless it sits in [1, 64] MB — the production default is 4 MB and 64 MB leaves generous headroom for the largest bulk historical chunk — and the conversion uses checked_mul so an out-of-range input always surfaces as a range error.

streaming.data_watchdog_ms

This knob had no bounds:: range and no clause in validate, unlike every sibling streaming knob. It is a wall-clock backstop above the read timeout, so an enabled value must sit in [100, 3_600_000] ms (one-hour ceiling) and at or above timeout_ms, otherwise the backstop would fire before the read timeout it is meant to backstop. 0 continues to disable it. Both invariants are now enforced.

ring_size upper bound

check_ring_size enforced power-of-two and a 64-slot minimum but capped nothing above it, so ring_size = 2^40 passed and the engine would attempt to pre-allocate terabytes up front. The ring is allocated in full at construction, so the size is a memory commitment, not a load-driven ceiling. A 2^24 (16,777,216-slot) maximum bounds that commitment while leaving generous headroom above the shipped 131,072-slot default.

Tests

  • grpc_max_message_size_mb_at_ceiling_is_accepted / _above_ceiling_is_rejected / _zero_is_rejected / _absurd_value_does_not_panic_or_wrap
  • validate_accepts_disabled_data_watchdog / validate_accepts_in_range_data_watchdog / validate_rejects_data_watchdog_below_read_timeout / validate_rejects_data_watchdog_above_maximum
  • accepts_maximum / rejects_above_maximum / error_message_names_maximum_on_too_large / shipped_default_is_under_maximum

cargo fmt --all -- --check, cargo test -p thetadatadx --lib --features config-file (853 passed), and cargo clippy -p thetadatadx --features config-file --lib -- -D warnings all pass.

🤖 Generated with Claude Code

Three configuration knobs accepted out-of-range values that the loader then propagated into the engine as oversized memory commitments or arithmetic that wrapped under the operator's stated tuning.

`[grpc] max_message_size_mb` was multiplied by `1024 * 1024` straight from TOML with no range check, so an absurd value overflowed the byte conversion into a tiny garbage cap and a `0` silently uncapped the inbound decode budget. The override is now rejected by name unless it sits in `[1, 64]` MB, and the conversion uses `checked_mul` so an out-of-range input always surfaces as a range error.

`data_watchdog_ms` had no `bounds::` range and no `validate` clause, unlike every sibling streaming knob. It is a wall-clock backstop above the read timeout, so an enabled value must sit in `[100, 3_600_000]` ms and at or above `timeout_ms`; `0` continues to disable it. Both invariants are now enforced.

`check_ring_size` capped nothing above the power-of-two minimum, so `ring_size = 2^40` passed validation and the engine would pre-allocate an absurd ring. A `2^24` ceiling bounds the up-front allocation while leaving generous headroom above the shipped 131,072-slot default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@userFRM userFRM enabled auto-merge (squash) June 16, 2026 15:41
@userFRM userFRM merged commit bcd4dd4 into main Jun 16, 2026
40 of 45 checks passed
@userFRM userFRM deleted the fix/config-bounds-hardening branch June 16, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants