Skip to content

Commit 4766dce

Browse files
committed
Fix choice of comparison operators for cross-type hashed subplans.
Commit bf6c614 rearranged the lookup of the comparison operators needed in a hashed subplan, and in so doing, broke the cross-type case: it caused the original LHS-vs-RHS operator to be used to compare hash table entries too (which of course are all of the RHS type). This leads to C functions being passed a Datum that is not of the type they expect, with the usual hazards of crashes and unauthorized server memory disclosure. For the set of hashable cross-type operators present in v11 core Postgres, this bug is nearly harmless on 64-bit machines, which may explain why it escaped earlier detection. But it is a live security hazard on 32-bit machines; and of course there may be extensions that add more hashable cross-type operators, which would increase the risk. Reported by Andreas Seltenreich. Back-patch to v11 where the problem came in. Security: CVE-2019-10209
1 parent ffa2d37 commit 4766dce

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

src/backend/executor/nodeSubplan.c

+10-5
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
860860
i;
861861
TupleDesc tupDescLeft;
862862
TupleDesc tupDescRight;
863+
Oid *cross_eq_funcoids;
863864
TupleTableSlot *slot;
864865
List *oplist,
865866
*lefttlist,
@@ -923,6 +924,9 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
923924
sstate->tab_collations = (Oid *) palloc(ncols * sizeof(Oid));
924925
sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
925926
sstate->cur_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
927+
/* we'll need the cross-type equality fns below, but not in sstate */
928+
cross_eq_funcoids = (Oid *) palloc(ncols * sizeof(Oid));
929+
926930
i = 1;
927931
foreach(l, oplist)
928932
{
@@ -952,7 +956,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
952956
righttlist = lappend(righttlist, tle);
953957

954958
/* Lookup the equality function (potentially cross-type) */
955-
sstate->tab_eq_funcoids[i - 1] = opexpr->opfuncid;
959+
cross_eq_funcoids[i - 1] = opexpr->opfuncid;
956960
fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
957961
fmgr_info_set_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]);
958962

@@ -961,7 +965,9 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
961965
NULL, &rhs_eq_oper))
962966
elog(ERROR, "could not find compatible hash operator for operator %u",
963967
opexpr->opno);
964-
fmgr_info(get_opcode(rhs_eq_oper), &sstate->tab_eq_funcs[i - 1]);
968+
sstate->tab_eq_funcoids[i - 1] = get_opcode(rhs_eq_oper);
969+
fmgr_info(sstate->tab_eq_funcoids[i - 1],
970+
&sstate->tab_eq_funcs[i - 1]);
965971

966972
/* Lookup the associated hash functions */
967973
if (!get_op_hash_functions(opexpr->opno,
@@ -1003,16 +1009,15 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
10031009

10041010
/*
10051011
* Create comparator for lookups of rows in the table (potentially
1006-
* across-type comparison).
1012+
* cross-type comparisons).
10071013
*/
10081014
sstate->cur_eq_comp = ExecBuildGroupingEqual(tupDescLeft, tupDescRight,
10091015
&TTSOpsVirtual, &TTSOpsMinimalTuple,
10101016
ncols,
10111017
sstate->keyColIdx,
1012-
sstate->tab_eq_funcoids,
1018+
cross_eq_funcoids,
10131019
sstate->tab_collations,
10141020
parent);
1015-
10161021
}
10171022

10181023
return sstate;

src/test/regress/expected/subselect.out

+24
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,30 @@ select * from outer_text where (f1, f2) not in (select * from inner_text);
764764
b |
765765
(2 rows)
766766

767+
--
768+
-- Another test case for cross-type hashed subplans: comparison of
769+
-- inner-side values must be done with appropriate operator
770+
--
771+
explain (verbose, costs off)
772+
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
773+
QUERY PLAN
774+
-------------------------------------
775+
Result
776+
Output: (hashed SubPlan 1)
777+
SubPlan 1
778+
-> Append
779+
-> Result
780+
Output: 'bar'::name
781+
-> Result
782+
Output: 'bar'::name
783+
(8 rows)
784+
785+
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
786+
?column?
787+
----------
788+
f
789+
(1 row)
790+
767791
--
768792
-- Test case for premature memory release during hashing of subplan output
769793
--

src/test/regress/sql/subselect.sql

+10
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,16 @@ insert into inner_text values ('a', null);
452452

453453
select * from outer_text where (f1, f2) not in (select * from inner_text);
454454

455+
--
456+
-- Another test case for cross-type hashed subplans: comparison of
457+
-- inner-side values must be done with appropriate operator
458+
--
459+
460+
explain (verbose, costs off)
461+
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
462+
463+
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
464+
455465
--
456466
-- Test case for premature memory release during hashing of subplan output
457467
--

0 commit comments

Comments
 (0)