Skip to content

Shared per-instance encryption_factory mutex can be overwritten mid-scan (cross-scan key misuse) #195

Description

@shefeek-jinnah

Surfaced by an automated multi-agent correctness audit.

Summary

DuckLakeTable holds the encryption factory as a single per-instance shared slot (Arc<Mutex<Option<Arc<dyn EncryptionFactory>>>>). scan() rebuilds and unconditionally overwrites it per metadata page, then reads it later when building each page's ParquetSource — so concurrent or self-referencing scans on the same instance can clobber each other's decryption keys.

Location

  • src/table.rs:662 (shared field)
  • src/table.rs:1016-1038 (configure_encryption_factory unconditional overwrite)
  • src/table.rs:1007-1013 (create_parquet_source reads current slot)
  • scan loop ~src/table.rs:2382-2509

Failure scenario

Two scan() calls interleave on one shared DuckLakeTable Arc (e.g. a self-join, or concurrent queries): scan A configures the factory for its page and awaits; scan B overwrites the slot for a different page; scan A's create_parquet_source then uses B's key set → decryption failure, or (on file-path collision) decryption with the wrong key.

Suggested fix

Build a per-scan-page factory and thread it directly into ParquetSource construction instead of a single shared mutable slot on the table instance.

Severity: High for encrypted tables under concurrent/self-join scans; feature-gated (encryption), narrower blast radius.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghighsecurity-reviewFound during security review

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions