Skip to content

CAS — consolidated static analysis audit findings (tracking) #2031

Description

@vzakaznikov

CAS (metadata_type = content_addressed MergeTree backend) — consolidated audit tracking

This is a tracking issue for a static-analysis audit of the Content-Addressed Storage (CAS) MergeTree
disk backend. It consolidates 39 audit reports into a single deduplicated checklist of 131 distinct
findings
, each with a unique CAS-### id.

Important

This is a static/logical review. Many items are expected to be by-design, not-a-bug, latent, or
already-handled.
The checklist is meant to be triaged item-by-item — please dismiss or resolve freely.

📎 Full audit reports (all 39 .md files + the consolidated summary)

All source reports live in this gist:
https://gist.github.com/vzakaznikov/8b0506a495187ce3d634385544beebea
(the consolidated table with full descriptions and the original-id → CAS-id cross-reference is
audit-summary.md in that gist).

How to triage

For each item, when reviewed:

  1. Check the box once it is triaged (resolved, dismissed, or filed as its own issue).
  2. Replace resolution: TBD inline with a verdict, e.g.
    ✅ fixed (#PR) · 🛠 will-fix · ❌ wontfix · 🚫 not-a-bug · 📐 by-design · 🟡 needs-repro · ↗ split-out (#NNN).
  3. Add reasoning as a comment referencing the CAS-### id (comments are the discussion trail; the
    checklist line is the short status).

Severity is the highest assigned by any source audit. Class tags:
DATA-LOSS · LEAK · LIVENESS · CONCURRENCY · INTEGRITY · SECURITY · DECODE/DoS · COMPAT · FEATURE-GAP · PERF/SCALE · OBSERV/DAY2 · COMPLIANCE · CONFIG · TEST-GAP · CORRECTNESS.


🔴 High

  • CAS-001 Reader holds no pin across the deferred blob GET → dropRef+GC delete can 404 a committed blob mid-query (ref-less/cross-node) · DATA-LOSS — resolution: TBD
  • CAS-002 Shard casPut fenced by content token, not writer_epoch → pause/clone/skew split-brain zombie write · SECURITY/CORRECTNESS — resolution: TBD
  • CAS-003 Non-cryptographic content hash (CityHash128) + reads never re-verify → collision poisoning via pool-global dedup · SECURITY/INTEGRITY — resolution: TBD
  • CAS-004 No intra-pool authorization; identities self-asserted (bucket credential = whole perimeter) · SECURITY — resolution: TBD
  • CAS-005 Blob payload never re-hashed vs logical_hash on read → silent S3 bit-rot undetected by CAS · INTEGRITY — resolution: TBD
  • CAS-006 CAS durable publish runs under DataPartsLock → table-wide writer/DDL stall under S3 latency/throttling · LIVENESS/PERF — resolution: TBD
  • CAS-007 UniqueKey/upsert DeleteBitmap + SSTIndex hot-rewrite (non-mutable-set file) → per-update whole-part republish; untested · FEATURE-GAP/PERF — resolution: TBD
  • CAS-008 Untrimmed root-shard journal under churn hits 64 MiB hard limit → writes rejected until GC folds (write↔GC liveness coupling) · LIVENESS/SCALE — resolution: TBD
  • CAS-009 Rolling upgrade across a format-gen bump breaks old nodes (compatibility_version always = G_BUILD, write-down-to-floor unimplemented) · COMPAT — resolution: TBD
  • CAS-010 No coverage-guided fuzzing of any CAS decoder (untrusted shared-pool bytes) · TEST-GAP/DoS — resolution: TBD
  • CAS-011 Bucket versioning / GCS soft-delete silently breaks GC reclaim; fails open on inconclusive check · OBSERV/LEAK — resolution: TBD
  • CAS-012 Native conditional-write path not e2e tested on real S3/GCS (only RustFS+emu) · TEST-GAP — resolution: TBD
  • CAS-013 fsck (reachability/dangling/physical-vs-logical) not operator-accessible via SQL · OBSERV/DAY2 — resolution: TBD
  • CAS-014 No GC-liveness / reclaim-backlog / physical-bytes metric (silent "GC stopped" invisible) · OBSERV/DAY2 — resolution: TBD
  • CAS-015 GC REBUILD has no mount-lease interlock → run on a live pool can condemn/delete live blobs · DATA-LOSS — resolution: TBD
  • CAS-016 Lifecycle expiration rule deletes live blobs → dangling refs/data loss; unguarded · DATA-LOSS/CONFIG — resolution: TBD
  • CAS-017 Object Lock/WORM/retention forbids overwriting mutable root-shards & gc/state → writes+GC break; unguarded · CONFIG/LIVENESS — resolution: TBD
  • CAS-018 No bounded delete→physical-erase SLA (GC-deferred, stallable) → cannot promise GDPR/CCPA deadlines · COMPLIANCE — resolution: TBD
  • CAS-019 Dedup: one owner's delete erases nothing while any ref shares the blob; no per-subject shred · COMPLIANCE — resolution: TBD
  • CAS-046 DiskEncrypted random-IV ciphertext defeats content-addressed dedup (nullifies CAS; use S3 SSE) · FEATURE-GAP — resolution: TBD

🟠 Medium

  • CAS-020 promote-overwrite leaks the prior committed manifest (RENAME/lost-ACK); only non-reclaimed orphan class · LEAK — resolution: TBD
  • CAS-021 Multi-part commit() not atomic → durable partial commit on power-loss/compounded failure; failed rollback is silent · CORRECTNESS — resolution: TBD
  • CAS-022 RENAME TABLE (moveDirectory) non-atomic → split table on crash; no durable move-journal/auto-re-drive · CORRECTNESS — resolution: TBD
  • CAS-023 Teardown UAF / std::terminate race (scheduleRemount ignores remount_stop) · CONCURRENCY — resolution: TBD
  • CAS-024 locate() uses fixed PoolMeta.blob_header_len, not the blob's own envelope header_len → wrong offset/misread · CORRECTNESS — resolution: TBD
  • CAS-025 PartManifest.payload_digest written but never re-verified on decode/read · INTEGRITY — resolution: TBD
  • CAS-026 Protobuf ParseFromArray(…, static_cast<int>(size)), no size cap → OOM / negative-size on oversized/planted object · DECODE/DoS — resolution: TBD
  • CAS-027 Additive protobuf fields dropped on re-encode by older build → mixed-version control-plane data loss · COMPAT/DATA-LOSS — resolution: TBD
  • CAS-028 RunFileReader::next() klen/plen unchecked operator[]/substr → OOB read on CRC-valid malformed block · DECODE/DoS — resolution: TBD
  • CAS-029 VM-clone/snapshot split brain (shared server_uuid), bounded only by renew period · CORRECTNESS — resolution: TBD
  • CAS-030 Wall-clock mount-lease expiry vs boot-clock fence → clock-skew reclaim (NTP-spoof amplifiable) · CORRECTNESS/SECURITY — resolution: TBD
  • CAS-031 Relink/rename receiver trusts sender blob_size/path (payload_digest ignored) → wrong-length reads · INTEGRITY — resolution: TBD
  • CAS-032 Zombie GC leader's unconditional pulseHeartbeat clobbers gc/hb.owner → lease steal from live leader · LIVENESS — resolution: TBD
  • CAS-033 Persistent shard clamp → pool-wide suppress_destructive halts all reclamation (no self-heal) · LIVENESS — resolution: TBD
  • CAS-034 Coalesced shard read has no deadline → hung leader GET blocks all followers (reader convoy) · LIVENESS — resolution: TBD
  • CAS-035 Presence-asserting closures misreport a lost-ACK-succeeded write as failure · CORRECTNESS — resolution: TBD
  • CAS-036 blob_header_len floor (96) below mandatory provenance-TLV need → configuring 96–120 bricks all blob writes · CONFIG — resolution: TBD
  • CAS-037 Content-hash algorithm is an unversioned/unpinned pool contract → a future algo change forks dedup + breaks relink · INTEGRITY/COMPAT — resolution: TBD
  • CAS-038 Scratch temp file un-fsynced and never verified vs its key between hash and upload → silent wrong-bytes blob · INTEGRITY — resolution: TBD
  • CAS-039 Envelope size-consistency check bypassable via logical_size uint64 overflow wrap · DECODE — resolution: TBD
  • CAS-040 system.parts.bytes_on_disk is logical, over-reports physical N× under dedup; no physical/dedup view · OBSERV — resolution: TBD
  • CAS-041 Cross-disk MOVE PARTITION TO DISK/VOLUME unverified + byte-copies (no relink even CAS→CAS same-pool) · FEATURE-GAP/PERF — resolution: TBD
  • CAS-042 BACKUP is Atomic-DB-only (Ordinary/non-UUID rejected); incremental dedup + RESTORE round-trip untested · FEATURE-GAP — resolution: TBD
  • CAS-043 DROP/TRUNCATE/TTL-move frees zero bytes synchronously (GC-deferred; leaks if GC off); TTL move double-bills · LEAK/OBSERV — resolution: TBD
  • CAS-044 Crash between catalog drop and dropNamespace → permanently orphaned namespace; no reconcile · LEAK — resolution: TBD
  • CAS-045 ZK part-set vs CAS ref diverge on partial commit (broken part or invisible live-ref leak) · CORRECTNESS/LEAK — resolution: TBD
  • CAS-047 Two "size" semantics (payload-only vs envelope+payload); direct getStorageObjects read = envelope-as-data · CORRECTNESS — resolution: TBD
  • CAS-048 getLastModified = publish time (resets on relink)/epoch(0)/throws → replica divergence, part-check skew · CORRECTNESS — resolution: TBD
  • CAS-049 Decode caches wholesale-clear at 16384 (no LRU) → thundering-herd re-decode cliff at scale · PERF/SCALE — resolution: TBD
  • CAS-050 GC REBUILD zero-condemn scan is O(all blobs) with sync HEAD each, unbudgeted (millions of RTs at DR time) · PERF/SCALE — resolution: TBD
  • CAS-051 Cross-region replication accumulates un-GC'd shadow bucket; failover token/ETag-incoherent · CORRECTNESS/LEAK — resolution: TBD
  • CAS-052 Archive-tier transition (Glacier/IA) leaves cold blobs unreadable; no restore-and-retry · FEATURE-GAP — resolution: TBD
  • CAS-053 Throttle/429 storms compound with CAS-conflict retries (no CAS-level adaptive backoff) · LIVENESS — resolution: TBD
  • CAS-054 Relink cookie value not validated (only presence); framing an implicit wire contract → future v2 mis-read · COMPAT — resolution: TBD
  • CAS-055 Non-MergeTree engines / tmp disks / SSD-cache dicts / Distributed spool ungated on a CAS disk · CONFIG/FEATURE-GAP — resolution: TBD
  • CAS-056 root_shards is a fixed pool-wide create-time constant (no live reshard); same-shard writes CAS-bound · SCALE/CONFIG — resolution: TBD
  • CAS-057 GC discovery LIST is O(namespaces × shards) — round cost scales with pool size, not churn · PERF/SCALE — resolution: TBD
  • CAS-058 Read-your-writes/strongly-consistent LIST is a hard per-backend assumption (S3 ok, others unconfirmed) · COMPAT — resolution: TBD
  • CAS-059 MergeTree experimental transactions (txn_version MVCC) untested on CAS; multi-part visibility inherits CAS-021 · TEST-GAP/FEATURE-GAP — resolution: TBD
  • CAS-060 Failed-build debris reclaimed only by sweeps; failure storms → transient bloat · LEAK/LIVENESS — resolution: TBD
  • CAS-061 Full-text (GIN/Text) & vector-similarity index build/merge/read on CAS untested · FEATURE-GAP — resolution: TBD
  • CAS-062 No lease/owner introspection or documented force-release/recovery runbook · OBSERV/DAY2 — resolution: TBD
  • CAS-063 No PoolMeta/control-plane backup-restore story (corrupt PoolMeta fails mount closed) · OBSERV/DAY2 — resolution: TBD
  • CAS-064 server_root_id uniqueness operator-owned; collision → outage, reuse → stale owner/epoch · CONFIG — resolution: TBD
  • CAS-065 Azure/non-S3 object stores effectively unsupported for Native CAS (falls to unsafe Emulated) · COMPAT — resolution: TBD
  • CAS-066 createOrValidate silently ignores passed root_shards/blob_header_len when a pool exists · CONFIG — resolution: TBD
  • CAS-067 No read-side blob cache/pin — cold reads re-GET each blob · PERF — resolution: TBD
  • CAS-068 FS-cache-over-CAS caches whole-blob (envelope-inclusive) ranges; envelope-offset alignment untested · TEST-GAP — resolution: TBD
  • CAS-069 Migration onto/off CAS always a full rewrite; double-bills; a CAS volume narrows the whole table's ALTER surface · FEATURE-GAP/PERF — resolution: TBD
  • CAS-070 FREEZE shadow refs, detached refs, gc/snap retention silently retain deleted data/metadata · COMPLIANCE — resolution: TBD
  • CAS-071 No crypto-shred; physical erasure depends on backend DELETE (versioning/soft-delete/CRR retain copies) · COMPLIANCE — resolution: TBD
  • CAS-093 fsck detects Dangling (=already-lost) but never repairs; no forced cadence · INTEGRITY/DAY2 — resolution: TBD
  • CAS-113 DiskEncrypted-over-CAS: control-plane metadata plaintext, read-path untested/unguarded, cross-replica dedup lost · SECURITY/TEST-GAP — resolution: TBD

🟡 Low / hardening

  • CAS-072 Post-CAS T0 hand-off crash permanently strands a gc/gen/<g>/ prefix (fsck-only, no auto-reclaim) · LEAK — resolution: TBD
  • CAS-073 looksLikePartDir false-positive on non-Atomic names ending in three numeric groups → misroute · CORRECTNESS — resolution: TBD
  • CAS-074 checkNamespace/mountpointObjectKey don't reject ./.. → traversal risk on normalizing backends · SECURITY — resolution: TBD
  • CAS-075 Envelope header_hash covers only the 94-B core, not TLVs; critical-ext relies on writer honesty · INTEGRITY — resolution: TBD
  • CAS-076 FormatId::Roster defined but magicFor(Roster) throws → dead/incomplete path · CORRECTNESS — resolution: TBD
  • CAS-077 decodeFoldSeal casts enums without validation (unlike sibling decoders) · DECODE — resolution: TBD
  • CAS-078 Concurrent probes of a shared probe_prefix can spuriously read NOT_IMPLEMENTED · CORRECTNESS — resolution: TBD
  • CAS-079 Non-atomic HEAD-then-GET can pair old token with new bytes for a mutable object · CORRECTNESS — resolution: TBD
  • CAS-080 allocateWriterEpoch no overflow guard; fresh mount pins GC floor to 0 until first renewal · CORRECTNESS — resolution: TBD
  • CAS-081 abandon retires build_seq before the precommit-removal event (fragile ordering) · CORRECTNESS — resolution: TBD
  • CAS-082 Lost-ACK replay double-appends journal events (set-idempotent → bloat only) · LEAK — resolution: TBD
  • CAS-083 Flat-combining leader convoy + batch-wide failure amplification under S3 stall · LIVENESS — resolution: TBD
  • CAS-084 Orphan multipart uploads / ownerless manifest bodies on interrupt; CAS neither aborts nor reports MPUs · LEAK/DAY2 — resolution: TBD
  • CAS-085 allow_stale TTL ↔ GC condemn→delete latency coupling is a convention, not an invariant · CORRECTNESS — resolution: TBD
  • CAS-086 readManifest HEAD+GET not coalesced / absence not negatively cached → HEAD+GET storm · PERF — resolution: TBD
  • CAS-087 Force-fresh read isn't fresh on eventually-consistent backends · COMPAT — resolution: TBD
  • CAS-088 Lost/corrupt GC-internal artifacts wedge GC until manual GC REBUILD (by-design fail-stop) · LIVENESS — resolution: TBD
  • CAS-089 Regular-round mass-drop delta is non-streaming in-memory (rebuild is batched; round isn't) · PERF/SCALE — resolution: TBD
  • CAS-090 mount_keeper unique_ptr reassigned without sync vs renewWatermarkOnce (latent UAF) · CONCURRENCY — resolution: TBD
  • CAS-091 event_sink_ published after keeper thread start (std::function data race; timing-masked) · CONCURRENCY — resolution: TBD
  • CAS-092 shard_write_seq never pruned on dropNamespace → unbounded growth by lifetime (ns, shard) pairs · LEAK — resolution: TBD
  • CAS-094 No proactive scrubbing of cold blobs; one rotted shared blob damages every deduped ref · INTEGRITY — resolution: TBD
  • CAS-095 Fragile read-window arithmetic (resizeWorkingBuffer underflow; SEEK_CUR; plan trusts manifest offset/length) · DECODE — resolution: TBD
  • CAS-096 Scratch-FS-full fails insert late (no pre-flight); temp-file uniqueness relies on a random string · CORRECTNESS — resolution: TBD
  • CAS-097 updateRefPayload one-shots not rolled back ("commit failed" ≠ "no effect"); refs transiently observable in rollback window · CORRECTNESS — resolution: TBD
  • CAS-098 Wide-part read branches (inline vs blob, mid-stream mark narrowing, projection nested-key) need explicit coverage · TEST-GAP — resolution: TBD
  • CAS-099 setLastModified no-op ("touch to refresh age" fails); clearOldTemporaryDirectories inert (GC is the reaper) · CORRECTNESS — resolution: TBD
  • CAS-100 Soft-limit backpressure (≤1 s) can't prevent the hard-limit wedge; no per-tenant quota in a shared pool · SCALE — resolution: TBD
  • CAS-101 System-table quirks (empty remote_path, many-to-one paths, placeholder free space, unverified fields) · OBSERV — resolution: TBD
  • CAS-102 Relink vs byte-fetch indistinguishable in metrics; cache observability by blob key not part path · OBSERV — resolution: TBD
  • CAS-103 Move-vs-concurrent-GC untested (R1/X1 class); move_factor heuristics inert on CAS source · TEST-GAP — resolution: TBD
  • CAS-104 Non-replicated dedup-log durability rides mutable-file commit → bounded duplicate part on crash · CORRECTNESS — resolution: TBD
  • CAS-105 RESTORE round-trip + Packed storage-type parts (via RESTORE/ATTACH) untested/unsupported · TEST-GAP/FEATURE-GAP — resolution: TBD
  • CAS-106 GC cadence/retention knobs directly gate reclaim latency (LC-1) · CONFIG — resolution: TBD
  • CAS-107 Big-endian would silently fork dedup (no LE guard); manifest bytes/ManifestId not version-stable (harmless) · COMPAT — resolution: TBD
  • CAS-108 GC REBUILD DoS/FORCE blast radius; interrupted rebuild leaks un-swept gc/gen artifacts + ratchets generation · DAY2/LEAK — resolution: TBD
  • CAS-109 System log tables on CAS = tiny-part storm; tooling / EXCHANGE TABLES / disk-layering over CAS untested · PERF/TEST-GAP — resolution: TBD
  • CAS-110 FETCH-to-detached never relinks (full byte transfer); quorum/SYNC REPLICA/cloneReplica untested · PERF/TEST-GAP — resolution: TBD
  • CAS-111 Committed single-file unlinkFile is a fail-open no-op — bug if a future path deletes one committed file · CORRECTNESS — resolution: TBD
  • CAS-112 chmod / generateObjectKeyForPath throw NOT_IMPLEMENTED (latent; not called on CA today) · FEATURE-GAP — resolution: TBD
  • CAS-114 Storage-class cost/latency skew from tiering; CAS sets no storage class (bucket default applies) · CONFIG — resolution: TBD
  • CAS-115 Manifest duplicate-path detection is adjacent-only (prev_path, valid only because encode sorts) → non-adjacent dup paths in a corrupt/unsorted embedded RunFile pass decode undetected · DECODE / INTEGRITY — resolution: TBD
  • CAS-116 Per-file lookupPath/listDirectory are linear scans → O(entries²) to read all files of a wide part (thousands of columns ⇒ millions of comparisons); fix: index entries by path · PERF/SCALE — resolution: TBD
  • CAS-117 FINAL / parallel-replica reads / lightweight-update patch-apply-on-read untested for correctness-under-concurrent-merge (read-amplification + decode-cache interaction) · TEST-GAP — resolution: TBD

⚪ Info / by-design / verified-safe (non-actionable — for the record)

  • CAS-201 B151 early publish exposes a rollback-window read; multi-part commit non-atomicity documented · INFO — resolution: by-design
  • CAS-202 CAS is fully data-type agnostic (all MergeTree column types incl. JSON/Variant/Dynamic/QBit/Geo) · INFO — resolution: verified
  • CAS-203 All mainstream MergeTree part types supported (Wide/Compact/projection/patch/detached/frozen/Replicated relink) · INFO — resolution: verified
  • CAS-204 S3 SSE (SSE-S3/KMS/C) fully supported and recommended (transparent, preserves dedup) · INFO — resolution: verified
  • CAS-205 Safety core fail-closed everywhere (two-phase spine, ack-floor, exact-token deletes, baseline guard, TLA+ sabotage-validated) · INFO — resolution: verified
  • CAS-206 GC REBUILD --force correctly narrow (never bypasses lease/missing-manifest refusals) · INFO — resolution: verified
  • CAS-207 Content-addressed keys make FS cache ideal; one file = one blob ⇒ no cross-blob compression-boundary hazard · INFO — resolution: verified
  • CAS-208 TTL is data-driven, not mtime-driven → synthetic mtime does not affect TTL · INFO — resolution: verified
  • CAS-209 Relink is data-safe under version skew (fail-closed publish-nothing → byte-fetch fallback) · INFO — resolution: verified
  • CAS-210 Onto-CAS migration dedups on landing (storage-cost win) · INFO — resolution: verified
  • CAS-211 Provenance/CasEvent self-asserted (forgeable); blobs plaintext / content-equality observable (delegated to S3) · INFO — resolution: by-design
  • CAS-212 Retired FormatId values rely on "pre-release" — freeze the enum + reserved shapes at GA · INFO — resolution: TBD
  • CAS-213 manifestCleanupShard hashes the qualified ManifestId; GC-artifact determinism load-bearing/fail-closed · INFO — resolution: verified
  • CAS-214 Extensive instrumentation (66 ProfileEvents); classifyCasNs unanchored substring match (metric-only) · INFO — resolution: verified

Genuine data-loss / correctness paths (the short list to look at first)

CAS-001 (reader pin), CAS-002 (writer_epoch fencing — single highest-leverage fix, resolves most
Jepsen/security fencing findings), CAS-015 (GC REBUILD mount-lease interlock), CAS-016/CAS-017
(lifecycle expiration / Object Lock — config-triggered), and the integrity delegation CAS-005+CAS-003.
Everything else biases to a reclaimable leak, a liveness/operability cliff, or an unverified edge.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions