Skip to content

Broker resource limits are global, letting one session exhaust the budget for all others #1084

Description

@wdcui

Summary

BrokerCoreLimits are global to the broker core, but a single broker process serves every client association. One malicious or buggy local can therefore exhaust the shared budget and permanently deny object and pipe creation to all other sessions.

This is an availability issue only. Per-session ownership is enforced correctly on lookup, so there is no cross-session disclosure or confused-deputy exposure.

Details

The limits are explicitly documented as shared:

https://github.com/microsoft/litebox/blob/ulitebox/litebox_broker_core/src/lib.rs#L42-L45

/// Resource limits for broker-owned authority state.
///
/// These limits are global to the broker core, not per session.
pub struct BrokerCoreLimits {
    pub max_references: usize,          // DEFAULT: 4096
    pub max_total_pipe_capacity: usize, // DEFAULT: 64 MiB
}

BrokerCore is a process singleton (BROKER_CORE_CREATED.compare_exchange) that hands out sessions via create_session(), and both budgets are enforced against process-wide state:

  • create_object_reference / create_object_reference_pair check references.len() against max_references on the shared references map (litebox_broker_core/src/session.rs:82-101, :104-133).
  • Pipe capacity is reserved from the shared reserved_pipe_capacity: AtomicUsize against max_total_pipe_capacity (litebox_broker_core/src/pipe.rs:193-201).

Ownership enforcement itself is sound — with_authorized_object rejects handles whose session_id does not match the caller (litebox_broker_core/src/session.rs:173-188) — so this is strictly a shared-budget problem.

Impact

A local that creates 4096 object references, or reserves 64 MiB of pipe capacity, causes every subsequent create_* from any session to fail with ResourceExhausted. The attacker does not need to be clever; ordinary buggy behavior in one sandbox produces the same result.

Two properties make it worse:

  1. The budget is only released when the offending objects are closed or the session is torn down, so a local that simply stops making progress pins the budget indefinitely.
  2. The host's ring capacity waits (ControlRingProducer::wait_for_capacity, reached from UnixControlRingHostResponseSink::send_response) have no timeout. They are interruptible by shutdown or peer close, but a local that stays alive and refuses to drain its response ring holds its association — and its share of the global budget — indefinitely with nothing to evict it.

Suggested direction

Add per-session quotas alongside the existing global ceilings, so the global limit remains a backstop while no single session can consume all of it. Roughly:

  • Track live reference count and reserved pipe capacity per SessionId.
  • Enforce a per-session cap in create_object_reference, create_object_reference_pair, and the pipe capacity reservation path.
  • Release the per-session accounting on object close and on session teardown.

Bounding or timing out wait_for_capacity is not the right fix on its own: any threshold that evicts a malicious local will eventually evict a legitimately slow one. It is only relevant as a way to reclaim a pinned budget, which per-session quotas address more directly.

Notes

Pre-existing; not introduced by any current PR. Surfaced while auditing the broker trust boundary during review of #1083, which does not touch litebox_broker_core.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions