Repeated `INTERPOLATE` target: `interpolate_columns` OOB aborts the server
Describe what's wrong
SELECT ... ORDER BY x WITH FILL INTERPOLATE (c AS <expr>, c AS <expr>) - the same target column named twice - aborts the whole server with libc++ Hardening assertion __n < size() failed: vector[] index out of bounds as soon as one fill row is generated. Any user who can run a SELECT can kill the server. NOT introduced by this PR: a stock 26.7.1.1 binary aborts identically. It is reported here because the PR edits the exact pairing this abort comes from and adds a guard for it at FillingTransform.cpp:357 that cannot catch this case.
- Root cause:
src/Processors/Transforms/FillingTransform.cpp:499indexesinterpolate_columnswith a counter bounded byinterpolate_block.columns()while the vector is sized frominterpolate_description->result_columns_order. Nothing enforces that the two agree: the ctor validates fill-vs-interpolate overlap (:387), sorting-prefix-vs-interpolate overlap (:372) and ORDER BY-vs-interpolate overlap (:270), but never rejects twoINTERPOLATEitems with the same target name, and the guard this PR adds at :357 only compares two counts that are equal by construction.
Why we believe this is a bug: FillingTransform ctor (src/Processors/Transforms/FillingTransform.cpp:352-353) builds one interpolate_column_positions entry per result_columns_order name; two INTERPOLATE items targeting c both resolve to the same header position, so the vector is as long as the target list. insertFromFillingRow (:495-499) then iterates i < interpolate_block.columns() - the width of the block interpolate_actions->execute produced - and indexes interpolate_columns[i]. With a repeated target the executed block is wider than the position vector and interpolate_columns[i] reads past the end.
Affected locations:
src/Processors/Transforms/FillingTransform.cpp:499—interpolate_columns[i]->insertFrom(...)bounded byinterpolate_block.columns()- the out-of-bounds readsrc/Processors/Transforms/FillingTransform.cpp:357— guard added by this PR; compares two counts that are structurally equal, so it never fires and does not catch a repeated targetsrc/Processors/QueryPlan/FillingStep.cpp:275— the PR's deserializer rebuildsresult_columns_orderfrom the DAG outputs with no duplicate-name rejection, so a client-supplied plan reaches the same abort
Impact: Remote denial of service: a single SELECT aborts the server process (SIGABRT), dropping every other in-flight query and connection. It killed this session's server. Reproduced on ClickHouse 26.7.1.1 and on this PR's build, so all supported release branches are affected.
Does it reproduce on most recent release?
Yes — confirmed on current master (commit 2d19d1b6e073).
How to reproduce
Reproducer-- Test: a repeated INTERPOLATE target must raise an error, not abort the server.
DROP TABLE IF EXISTS t_interpolate_dup;
CREATE TABLE t_interpolate_dup (a UInt64, b String) ENGINE = MergeTree ORDER BY a;
INSERT INTO t_interpolate_dup VALUES (0, 'x'), (4, 'y');
SELECT a, b FROM t_interpolate_dup ORDER BY a WITH FILL STEP 2 INTERPOLATE (b AS b, b AS b);
DROP TABLE IF EXISTS t_interpolate_dup;
Expected behavior
Expected output of the reproducer above:
A typed exception, e.g. `Code: 475. DB::Exception: Multiple INTERPOLATE for identical expressions is not supported. (INVALID_WITH_FILL_EXPRESSION)`, with the server still running.
Error message and/or stacktrace
Actual output of the reproducer above on master (2d19d1b6e073):
contrib/llvm-project/libcxx/include/__vector/vector.h:417: libc++ Hardening assertion __n < size() failed: vector[] index out of bounds
timeout: the monitored command dumped core
-- server-side record of the same abort (server_data_19030/server.log):
2026.09.17 04:00:52.928097 <Fatal> BaseDaemon: (version 26.10.1.5 (official build), git hash: 8910e5de7c1f49fef0abfd9a51d2db8f77ca4dd1) Received sig
Suggested fixReject a repeated INTERPOLATE target in the FillingTransform constructor, next to the existing overlap checks, e.g. INVALID_WITH_FILL_EXPRESSION: "Multiple INTERPOLATE for identical expressions is not supported" - mirroring the wording of the existing "Multiple WITH FILL for identical expressions is not supported in ORDER BY" (:313). Since this PR is already editing :352-360, the check fits there; FillingStep::deserialize (FillingStep.cpp:259-276) should reject the same shape with INCORRECT_DATA so a client-supplied plan cannot reach it either.
Same pattern as #111547 (found by: lexical, frames, vector, fix_path; adjacent version: 26.8), #111927 (found by: lexical, vector, fix_path; vector: cosine distance 0.31 (agrees with the lexical leg)).
Open risks:
- The analyzer accepts the duplicate silently; rejecting it in the transform changes a (currently crashing) query into an error, so a stricter rejection in
PlannerSorting/QueryAnalyzermay be the better home if maintainers prefer failing at analysis time.
Found during automated review of PR #116135; whether that PR introduced it could not be established, so nobody is tagged. Severity P0 · Finding h_pr116135_101
Source: ClickHouse/ClickHouse