Fix choice of comparison operators for cross-type hashed subplans.

Commit bf6c614a2 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
This commit is contained in:
Tom Lane 2019-08-05 11:20:21 -04:00
parent ffa2d37e5f
commit 4766dce0dd
3 changed files with 44 additions and 5 deletions

View File

@ -860,6 +860,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
i;
TupleDesc tupDescLeft;
TupleDesc tupDescRight;
Oid *cross_eq_funcoids;
TupleTableSlot *slot;
List *oplist,
*lefttlist,
@ -923,6 +924,9 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
sstate->tab_collations = (Oid *) palloc(ncols * sizeof(Oid));
sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
sstate->cur_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
/* we'll need the cross-type equality fns below, but not in sstate */
cross_eq_funcoids = (Oid *) palloc(ncols * sizeof(Oid));
i = 1;
foreach(l, oplist)
{
@ -952,7 +956,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
righttlist = lappend(righttlist, tle);
/* Lookup the equality function (potentially cross-type) */
sstate->tab_eq_funcoids[i - 1] = opexpr->opfuncid;
cross_eq_funcoids[i - 1] = opexpr->opfuncid;
fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
fmgr_info_set_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]);
@ -961,7 +965,9 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
NULL, &rhs_eq_oper))
elog(ERROR, "could not find compatible hash operator for operator %u",
opexpr->opno);
fmgr_info(get_opcode(rhs_eq_oper), &sstate->tab_eq_funcs[i - 1]);
sstate->tab_eq_funcoids[i - 1] = get_opcode(rhs_eq_oper);
fmgr_info(sstate->tab_eq_funcoids[i - 1],
&sstate->tab_eq_funcs[i - 1]);
/* Lookup the associated hash functions */
if (!get_op_hash_functions(opexpr->opno,
@ -1003,16 +1009,15 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
/*
* Create comparator for lookups of rows in the table (potentially
* across-type comparison).
* cross-type comparisons).
*/
sstate->cur_eq_comp = ExecBuildGroupingEqual(tupDescLeft, tupDescRight,
&TTSOpsVirtual, &TTSOpsMinimalTuple,
ncols,
sstate->keyColIdx,
sstate->tab_eq_funcoids,
cross_eq_funcoids,
sstate->tab_collations,
parent);
}
return sstate;

View File

@ -764,6 +764,30 @@ select * from outer_text where (f1, f2) not in (select * from inner_text);
b |
(2 rows)
--
-- Another test case for cross-type hashed subplans: comparison of
-- inner-side values must be done with appropriate operator
--
explain (verbose, costs off)
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
QUERY PLAN
-------------------------------------
Result
Output: (hashed SubPlan 1)
SubPlan 1
-> Append
-> Result
Output: 'bar'::name
-> Result
Output: 'bar'::name
(8 rows)
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
?column?
----------
f
(1 row)
--
-- Test case for premature memory release during hashing of subplan output
--

View File

@ -452,6 +452,16 @@ insert into inner_text values ('a', null);
select * from outer_text where (f1, f2) not in (select * from inner_text);
--
-- Another test case for cross-type hashed subplans: comparison of
-- inner-side values must be done with appropriate operator
--
explain (verbose, costs off)
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
select 'foo'::text in (select 'bar'::name union all select 'bar'::name);
--
-- Test case for premature memory release during hashing of subplan output
--