Skip to content

Commit a858327

Browse files
committed
Fix 32-bit build dangling pointer issue in WindowAgg
9d9c02c added window "run conditions", which allows the evaluation of monotonic window functions to be skipped when the run condition is no longer true. Prior to this commit, once the run condition was no longer true and we stopped evaluating the window functions, we simply just left the ecxt_aggvalues[] and ecxt_aggnulls[] arrays alone to store whatever value was stored there the last time the window function was evaluated. Leaving a stale value in there isn't really a problem on 64-bit builds as all of the window functions which we recognize as monotonic all return int8, which is passed by value on 64-bit builds. However, on 32-bit builds, this was a problem as the value stored in the ecxt_values[] element would be a by-ref value and it would be pointing to some memory which would get reset once the tuple context is destroyed. Since the WindowAgg node will output these values in the resulting tupleslot, this could be problematic for the top-level WindowAgg node which must look at these values to filter out the rows that don't meet its filter condition. Here we fix this by just zeroing the ecxt_aggvalues[] and setting the ecxt_aggnulls[] array to true when the run condition first becomes false. This results in the WindowAgg's output having NULLs for the WindowFunc's columns rather than the stale or pointer pointing to possibly freed memory. These tuples with the NULLs can only make it as far as the top-level WindowAgg node before they're filtered out. To ensure that these tuples *are* always filtered out, we now insist that OpExprs making up the run condition are strict OpExprs. Currently, all the window functions which the planner recognizes as monotonic return INT8 and the operator which is used for the run condition must be a member of a btree opclass. In reality, these restrictions exclude nothing that's built-in to Postgres and are unlikely to exclude anyone's custom operators due to the requirement that the operator is part of a btree opclass. It would be unusual if those were not strict. Reported-by: Sergey Shinderuk, using valgrind Reviewed-by: Richard Guo, Sergey Shinderuk Discussion: https://postgr.es/m/[email protected] Backpatch-through: 15, where 9d9c02c was added
1 parent 83a1a1b commit a858327

File tree

2 files changed

+32
-0
lines changed

2 files changed

+32
-0
lines changed

src/backend/executor/nodeWindowAgg.c

+20
Original file line numberDiff line numberDiff line change
@@ -2300,7 +2300,27 @@ ExecWindowAgg(PlanState *pstate)
23002300
continue;
23012301
}
23022302
else
2303+
{
23032304
winstate->status = WINDOWAGG_PASSTHROUGH;
2305+
2306+
/*
2307+
* If we're not the top-window, we'd better NULLify
2308+
* the aggregate results. In pass-through mode we no
2309+
* longer update these and this avoids the old stale
2310+
* results lingering. Some of these might be byref
2311+
* types so we can't have them pointing to free'd
2312+
* memory. The planner insisted that quals used in
2313+
* the runcondition are strict, so the top-level
2314+
* WindowAgg will filter these NULLs out in the filter
2315+
* clause.
2316+
*/
2317+
numfuncs = winstate->numfuncs;
2318+
for (i = 0; i < numfuncs; i++)
2319+
{
2320+
econtext->ecxt_aggvalues[i] = (Datum) 0;
2321+
econtext->ecxt_aggnulls[i] = true;
2322+
}
2323+
}
23042324
}
23052325
else
23062326
{

src/backend/optimizer/path/allpaths.c

+12
Original file line numberDiff line numberDiff line change
@@ -2399,6 +2399,18 @@ check_and_push_window_quals(Query *subquery, RangeTblEntry *rte, Index rti,
23992399
if (list_length(opexpr->args) != 2)
24002400
return true;
24012401

2402+
/*
2403+
* Currently, we restrict this optimization to strict OpExprs. The reason
2404+
* for this is that during execution, once the runcondition becomes false,
2405+
* we stop evaluating WindowFuncs. To avoid leaving around stale window
2406+
* function result values, we set them to NULL. Having only strict
2407+
* OpExprs here ensures that we properly filter out the tuples with NULLs
2408+
* in the top-level WindowAgg.
2409+
*/
2410+
set_opfuncid(opexpr);
2411+
if (!func_strict(opexpr->opfuncid))
2412+
return true;
2413+
24022414
/*
24032415
* Check for plain Vars that reference window functions in the subquery.
24042416
* If we find any, we'll ask find_window_run_conditions() if 'opexpr' can

0 commit comments

Comments
 (0)