Skip to content

Antalya 26.6, backport of 120098: Keep the target storage alive for a snapshot handed out by an Alias table - #2557

Merged
mkmkme merged 4 commits into
antalya-26.6from
bugfix/antalya-26.6/alias-snapshot-storage-holder
Oct 13, 2026
Merged

mkmkme merged 4 commits into
antalya-26.6from
bugfix/antalya-26.6/alias-snapshot-storage-holder

Conversation

@ianton-ru

@ianton-ru ianton-ru commented Oct 13, 2026 •

Copy link
Copy Markdown

Backport of ClickHouse#120098.

StorageSnapshot refers to its storage through a bare reference. Alias::getStorageSnapshot forwards to getTargetTable, and that call returns the owning pointer by value, so it dies at the end of the expression. The snapshot then refers to the target with no owner, while the query owns and share-locks the Alias instead. ASan reported this as a heap-use-after-free in QueryAnalyzer::initializeTableExpressionData for an Alias over a catalog Iceberg table. Upstream fixed the same lifetime gap after a TSan report during join reordering.

The upstream change adds withStorageHolder, which returns a new snapshot instead of writing the holder into a snapshot that other readers of the query may already share. An Alias chain keeps the first holder, so the pin stays on the innermost storage. clone copies the holder.

On this branch snapshot Data is still a unique_ptr. ClickHouse#110186 changed it to shared_ptr<const Data> on master, and ClickHouse#120098 then copies that pointer inside withStorageHolder. Pull request 110186 is not in antalya-26.6, so this backport cannot share Data the same way. withStorageHolder clones it instead, which adds clone on the MergeTree, Memory, and hybrid Distributed snapshot data. That is why this backport is larger than the original pull request.

Changelog category (leave one):

  • Critical Bug Fix (crash, data loss, RBAC)

Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):

Reading through an Alias no longer uses a freed target storage. The snapshot keeps that storage alive until the query releases it.

Documentation entry for user-facing changes

No user-facing documentation change.

CI/CD Options

Exclude tests:

  • Fast test
  • Integration Tests
  • Stateless tests
  • Stateful tests
  • Unit tests
  • Performance tests
  • Aarch64 tests
  • All with ASAN
  • All with TSAN
  • All with MSAN
  • All with UBSAN
  • All with Coverage
  • All Regression
  • Disable CI Cache

Regression jobs to run:

  • Fast suites (mostly <1h)
  • Aggregate Functions (2h)
  • Alter (1.5h)
  • Benchmark (30m)
  • CAS (content-addressed storage; Antalya only)
  • ClickHouse Keeper (1h)
  • Iceberg (2h)
  • LDAP (1h)
  • OAuth (5m)
  • Parquet (1.5h)
  • RBAC (1.5h)
  • SSL Server (1h)
  • S3 (2h)
  • S3 Export (2h)
  • Swarms (30m)
  • Tiered Storage (2h)

`StorageSnapshot` keeps a bare reference to its storage, and `Alias` returned that snapshot after the temporary from `getTargetTable` was destroyed. This brings `withStorageHolder` from upstream, cloning snapshot `Data` because it is still a `unique_ptr` on this branch.

https: //github.com/ClickHouse/pull/120098
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Workflow [PR], commit [fc346d8]

@ianton-ru ianton-ru changed the title Keep the target storage alive for a snapshot handed out by an Alias table Antalya 26.6, backport of 120098:Keep the target storage alive for a snapshot handed out by an Alias table Oct 13, 2026
`getStorageSnapshotWithoutData` had no pin test, and the data clone was only exercised with an empty snapshot. The stateless join now goes through a `View`, because an `Alias` of a `Distributed` table does not itself request filter collection.

Related: #2557
Co-authored-by: Cursor <cursoragent@cursor.com>
@ianton-ru ianton-ru changed the title Antalya 26.6, backport of 120098:Keep the target storage alive for a snapshot handed out by an Alias table Antalya 26.6, backport of 120098: Keep the target storage alive for a snapshot handed out by an Alias table Oct 13, 2026
A null snapshot made the pointer comparison pass even when `clone` dropped the field.

Related: #2557
Co-authored-by: Cursor <cursoragent@cursor.com>
@mkmkme
mkmkme merged commit d9c9701 into antalya-26.6 Oct 13, 2026
316 checks passed
@ianton-ru ianton-ru added the verified Approved for release label Oct 14, 2026
zvonand added a commit that referenced this pull request Oct 20, 2026
`StorageSnapshot` keeps a bare reference to its storage, and `Alias` returned that snapshot after the temporary from `getTargetTable` was destroyed. `DataLakeCatalog` builds a new storage on every lookup, so the analyzer read freed memory under ASan in `test_alias_iceberg_catalog_cluster`.

https://altinity-build-artifacts.s3.amazonaws.com/json.html?PR=2578&sha=d283c4830fe3a292b75b983c3887adc8641d6243&name_0=PR&name_1=Integration%20tests%20%28amd_asan_ubsan%2C%20db%20disk%2C%20old%20analyzer%2C%204%2F8%29
Related: #2578
Related: ClickHouse#120098
Related: #2557
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants