Skip to content

v2.1.2 — security & memory hardening

Choose a tag to compare

@iamaliybi iamaliybi released this 08 Jul 21:58
8f16040

Security & memory hardening from a comprehensive optimization/security audit. Two genuine authorization vulnerabilities fixed, two unbounded-growth vectors bounded. No wire or config compatibility change — a recommended upgrade for any deployment using ACLs or with churning topic namespaces.

Security fixes

  • Will-topic validation (fixes a $SYS / wildcard / NUL bypass). The Will Message topic was checked against ACLs but never against the concrete-topic validator that every live PUBLISH passes through. A client could set a retained will on the broker-reserved $SYS/... namespace (or a wildcard / NUL-bearing topic), then disconnect abnormally, and the broker would publish and retain the forged message — poisoning $SYS/# telemetry for every current and future subscriber. The will is now validated exactly like a live publish at CONNECT and dropped if invalid.
  • Subscribe-ACL wildcard-subsumption (fixes a privilege escalation). SUBSCRIBE authorization compared the requested filter with a matcher that treats its argument as a concrete topic — so a client granted home/+ could subscribe to home/# and receive the entire subtree. A new correct filter-subset test allows a request only if every topic it could match is also covered by an allow rule. Publish ACLs are unchanged.

Memory fixes

  • Topic trie prunes dead nodes; interned segments are reclaimed. UNSUBSCRIBE / disconnect emptied a node's subscriber list but never removed the empty nodes, so the trie grew with every distinct filter ever seen (common when filters embed per-client/correlation ids) and the segment interner held each segment forever. Removal now prunes empty nodes, and a periodic per-shard sweep reclaims unreferenced interned segments — both bounded to the live subscription set under churn.
  • Stale shared-subscription round-robin cursors are reclaimed by the same sweep.

Notes

The audit confirmed the message hot path is already well-optimized; the one remaining allocation (a per-subscriber client_id clone in route()) is documented but deliberately left unchanged pending a wide-fan-out benchmark, as eliminating it needs a delivery-path borrow-restructure not worth the regression risk without proof of the win.

Tests: +7 (120 unit + 23 integration). Adversarial battery still 12/12; clippy -D clean.

Assets: rusquitto-x86_64-unknown-linux-gnu, rusquitto-aarch64-unknown-linux-gnu (zig-built, glibc 2.31), and the annotated rusquitto.config.toml. Full details in CHANGELOG.md.