Skip to content

Fix ReadBuffer is canceled exception in zip archive operations#100400

Merged
alexey-milovidov merged 1 commit intomasterfrom
fix-readbuffer-canceled-in-zip-archive
Mar 23, 2026
Merged

Fix ReadBuffer is canceled exception in zip archive operations#100400
alexey-milovidov merged 1 commit intomasterfrom
fix-readbuffer-canceled-in-zip-archive

Conversation

@alexey-milovidov
Copy link
Copy Markdown
Member

When a ReadBuffer::nextImpl throws, ReadBuffer::next calls cancel() and re-throws. The minizip library catches the exception via the C callback (readFileFunc) which stores it and returns 0. However, minizip may then call readFileFunc again before the stored exception is re-thrown via rethrowStreamException. At that point the underlying ReadBuffer is already canceled, and the chassert(!isCanceled()) in ReadBuffer::next fires as a logical error.

The fix adds early-return checks for stored_exception in all minizip callback functions (readFileFunc, tellFunc, seekFunc, writeFileFunc) to avoid touching the buffer after a prior exception has already canceled it.

CI report: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=100395&sha=97c66cfd486cfc33344b02f7bf69a66ce1664104&name_0=PR&name_1=Stress%20test%20%28amd_msan%29

Changelog category (leave one):

  • Bug Fix (user-visible misbehavior in an official stable release)

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

Fix ReadBuffer is canceled. Can't read from it. exception in backup/restore operations using zip archives.

Documentation entry for user-facing changes

  • Documentation is written (mandatory for new features)

When a `ReadBuffer::nextImpl` throws, `ReadBuffer::next` calls `cancel()`
and re-throws. The minizip library catches the exception via the C callback
(`readFileFunc`) which stores it and returns 0. However, minizip may then
call `readFileFunc` again before the stored exception is re-thrown via
`rethrowStreamException`. At that point the underlying `ReadBuffer` is
already canceled, and the `chassert(!isCanceled())` in `ReadBuffer::next`
fires as a logical error.

The fix adds early-return checks for `stored_exception` in all minizip
callback functions (`readFileFunc`, `tellFunc`, `seekFunc`, `writeFileFunc`)
to avoid touching the buffer after a prior exception has already canceled it.

https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=100395&sha=97c66cfd486cfc33344b02f7bf69a66ce1664104&name_0=PR&name_1=Stress%20test%20%28amd_msan%29

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@clickhouse-gh
Copy link
Copy Markdown
Contributor

clickhouse-gh bot commented Mar 22, 2026

Workflow [PR], commit [25984e2]

Summary:


AI Review

Summary

This PR addresses a real exception path in zip callbacks by short-circuiting callback bodies when stored_exception is already set, which is directionally correct and matches the failure description. However, it currently lacks a focused regression test that exercises the exact callback-after-exception sequence, so long-term safety against regressions is not yet sufficient.

PR Metadata

⚠️ Changelog category is acceptable (Bug Fix).

⚠️ Changelog entry is present and user-facing, but could be slightly more specific about affected operations (BACKUP/RESTORE with zip) and that this is an exception-path fix.

Suggested replacement:
Fix ReadBuffer is canceled. Can't read from it.exception inBACKUP/RESTOREoperations that usezip archives when read callback failures are propagated through minizip.

Findings

⚠️ Majors

  • [src/IO/Archives/ZipArchiveReader.cpp:133] Missing regression test for the exact failure mode fixed here (callback invoked again after first exception is stored). Without this, future callback-flow refactors can reintroduce ReadBuffer is canceled. Can't read from it. in zip backup/restore paths.
    • Suggested fix: add a targeted stateless/integration test that forces a read exception inside zip-backed archive processing and verifies no follow-up callback touches a canceled ReadBuffer.

Tests

⚠️ Add one regression test that reproduces: first callback exception in zip read path, then additional callback invocation by minizip, and verify the resulting exception is the original stored one (not ReadBuffer is canceled. Can't read from it.).

ClickHouse Rules

Item Status Notes
Deletion logging
Serialization versioning
Core-area scrutiny
No test removal
Experimental gate
No magic constants
Backward compatibility
SettingsChangesHistory.cpp
PR metadata quality ⚠️ Changelog entry can be more specific for users
Safe rollout ⚠️ Missing regression test for exception path
Compilation time

Final Verdict

Status: ⚠️ Request changes

Minimum required actions:

  • Add a regression test for the callback-after-exception zip path fixed by this PR.

@clickhouse-gh clickhouse-gh bot added the pr-bugfix Pull request with bugfix, not backported by default label Mar 22, 2026
@tiandiwonder tiandiwonder self-assigned this Mar 23, 2026
@clickhouse-gh
Copy link
Copy Markdown
Contributor

clickhouse-gh bot commented Mar 23, 2026

LLVM Coverage Report

Metric Baseline Current Δ
Lines 83.90% 83.90% +0.00%
Functions 24.80% 24.70% -0.10%
Branches 76.50% 76.50% +0.00%

PR changed lines: PR changed-lines coverage: 85.71% (30/35, 0 noise lines excluded)
Diff coverage report
Uncovered code

Copy link
Copy Markdown
Contributor

@tiandiwonder tiandiwonder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simple an clean.

@alexey-milovidov alexey-milovidov merged commit 2cfd561 into master Mar 23, 2026
151 of 152 checks passed
@alexey-milovidov alexey-milovidov deleted the fix-readbuffer-canceled-in-zip-archive branch March 23, 2026 10:53
@robot-ch-test-poll1 robot-ch-test-poll1 added the pr-synced-to-cloud The PR is synced to the cloud repo label Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-bugfix Pull request with bugfix, not backported by default pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants