Skip to content

validate event type bounds in windowFunnel state deserialization - #107412

Merged
alexey-milovidov merged 4 commits into
ClickHouse:masterfrom
uwezkhan:windowfunnel-deserialize-event-bounds
Jun 22, 2026
Merged

validate event type bounds in windowFunnel state deserialization#107412
alexey-milovidov merged 4 commits into
ClickHouse:masterfrom
uwezkhan:windowfunnel-deserialize-event-bounds

Conversation

@uwezkhan

@uwezkhan uwezkhan commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

AddressSanitizer, getEventLevelNonStrictOnce on a crafted state whose single event byte is 255:

READ of size 1 ... AggregateFunctionWindowFunnel.cpp:347
  events_timestamp[event_idx - 1]   // event_idx = 254, buffer holds events_size elements

windowFunnel reads each event's condition index from the serialized state without a range check, then getEventLevelNonStrictOnce / getEventLevelStrictOnce use event - 1 to index events_timestamp / event_sequences (both sized events_size, the number of conditions, at most 32) and a std::array<UInt64, 32>. An event byte of 255 indexes about 250 elements past the buffer (out-of-bounds read, then a write at events_timestamp[event - 1]); an event byte of 0 with strict_order off indexes before it. The legitimate add path only produces events in [0, events_size], so deserialize is the only place such values enter. Reachable by any user:

SELECT finalizeAggregation(CAST(unhex('01010000000000000000000000FF') AS AggregateFunction(windowFunnel(1), UInt32, UInt8, UInt8)));

Before: out-of-range event types from a deserialized state reach the indexing loops.
After: deserialize rejects any event outside [1, events_size] (0 only with strict_order, the no-event sentinel) with INCORRECT_DATA; both the default and strict_once state layouts are covered.

The check is one linear pass over the deserialized events, negligible next to the deserialization itself.

Changelog category (leave one):

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

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

Fixed a heap buffer overflow (server crash) in windowFunnel when finalizing a crafted aggregate-function state with an out-of-range event type, reachable by any user with a single SELECT.

Version info

  • Backported to: 26.5.4.9, 26.4.5.81, 26.3.16.4, 25.8.25.33

@alexey-milovidov alexey-milovidov added the can be tested Allows running workflows for external contributors label Jun 14, 2026
@clickhouse-gh

clickhouse-gh Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [ea2e95b]

Summary:


AI Review

Summary

This PR hardens windowFunnel aggregate-state deserialization by rejecting event types outside the function's condition range while preserving the strict_order zero-event sentinel compatibility path. The current code and focused stateless test cover the default layout, strict_once, and the strict_order compatibility cases; I found no unresolved blocker or major issue.

Final Verdict

Status: ✅ Approve

@clickhouse-gh clickhouse-gh Bot added pr-critical-bugfix pr-must-backport Pull request should be backported intentionally. Use this label with great care! labels Jun 14, 2026

@alexey-milovidov alexey-milovidov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@alexey-milovidov alexey-milovidov self-assigned this Jun 14, 2026
uwezkhan added 2 commits June 16, 2026 00:36
The arm_tidy build failed with cppcoreguidelines-init-variables because
event_type was declared without an initializer before the if constexpr
branches assign it.
The validation deliberately keeps event type 0 valid under strict_order
because existing serialized states use it as the no-event sentinel. Add
positive finalizeAggregation cases for windowFunnel(1, 'strict_order')
and windowFunnel(1, 'strict_order', 'strict_once') so a regression that
moves min_event back to 1 would reject valid stored states and fail.
@uwezkhan

Copy link
Copy Markdown
Contributor Author

Also fixed the arm_tidy build failure: event_type was flagged by cppcoreguidelines-init-variables since it was declared before the if constexpr branches set it, so it's now initialized at declaration.

@clickhouse-gh

clickhouse-gh Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 85.30% 85.30% +0.00%
Functions 92.50% 92.60% +0.10%
Branches 77.50% 77.60% +0.10%

Changed lines: Changed C/C++ lines covered by tests: 12/12 (100.00%) | Lost baseline coverage (was covered on master, now uncovered in this PR): 1 line(s) · Uncovered code

Full report · Diff report

@alexey-milovidov
alexey-milovidov added this pull request to the merge queue Jun 22, 2026
Merged via the queue into ClickHouse:master with commit 20408b9 Jun 22, 2026
167 checks passed
robot-ch-test-poll4 added a commit that referenced this pull request Jun 22, 2026
Cherry pick #107412 to 25.8: validate event type bounds in windowFunnel state deserialization
robot-clickhouse added a commit that referenced this pull request Jun 22, 2026
robot-ch-test-poll4 added a commit that referenced this pull request Jun 22, 2026
Cherry pick #107412 to 26.3: validate event type bounds in windowFunnel state deserialization
robot-clickhouse added a commit that referenced this pull request Jun 22, 2026
robot-ch-test-poll4 added a commit that referenced this pull request Jun 22, 2026
Cherry pick #107412 to 26.4: validate event type bounds in windowFunnel state deserialization
robot-clickhouse added a commit that referenced this pull request Jun 22, 2026
robot-ch-test-poll4 added a commit that referenced this pull request Jun 22, 2026
Cherry pick #107412 to 26.5: validate event type bounds in windowFunnel state deserialization
robot-clickhouse added a commit that referenced this pull request Jun 22, 2026
@robot-clickhouse-ci-1 robot-clickhouse-ci-1 added the pr-synced-to-cloud The PR is synced to the cloud repo label Jun 22, 2026
@robot-ch-test-poll4 robot-ch-test-poll4 added the pr-must-backport-synced The `*-must-backport` labels are synced into the cloud Sync PR label Jun 22, 2026
@robot-clickhouse-ci-1 robot-clickhouse-ci-1 added the pr-backports-created Backport PRs are successfully created, it won't be processed by CI script anymore label Jun 22, 2026
alexey-milovidov added a commit that referenced this pull request Jun 23, 2026
Backport #107412 to 26.5: validate event type bounds in windowFunnel state deserialization
alexey-milovidov added a commit that referenced this pull request Jun 23, 2026
Backport #107412 to 26.3: validate event type bounds in windowFunnel state deserialization
alexey-milovidov added a commit that referenced this pull request Jun 23, 2026
Backport #107412 to 25.8: validate event type bounds in windowFunnel state deserialization
alexey-milovidov added a commit that referenced this pull request Jun 29, 2026
Backport #107412 to 26.4: validate event type bounds in windowFunnel state deserialization
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

can be tested Allows running workflows for external contributors pr-backports-created Backport PRs are successfully created, it won't be processed by CI script anymore pr-critical-bugfix pr-must-backport Pull request should be backported intentionally. Use this label with great care! pr-must-backport-synced The `*-must-backport` labels are synced into the cloud Sync PR 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.

4 participants