Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c33c423

Browse files
committed
Add security checks to selectivity estimation functions
Some selectivity estimation functions run user-supplied operators over data obtained from pg_statistic without security checks, which allows those operators to leak pg_statistic data without having privileges on the underlying tables. Fix by checking that one of the following is satisfied: (1) the user has table or column privileges on the table underlying the pg_statistic data, or (2) the function implementing the user-supplied operator is leak-proof. If neither is satisfied, planning will proceed as if there are no statistics available. At least one of these is satisfied in most cases in practice. The only situations that are negatively impacted are user-defined or not-leak-proof operators on a security-barrier view. Reported-by: Robert Haas <robertmhaas@gmail.com> Author: Peter Eisentraut <peter_e@gmx.net> Author: Tom Lane <tgl@sss.pgh.pa.us> Security: CVE-2017-7484
1 parent 3178f46 commit c33c423

File tree

7 files changed

+376
-32
lines changed

7 files changed

+376
-32
lines changed

doc/src/sgml/planstats.sgml

+60
Original file line numberDiff line numberDiff line change
@@ -448,4 +448,64 @@ rows = (outer_cardinality * inner_cardinality) * selectivity
448448

449449
</sect1>
450450

451+
<sect1 id="planner-stats-security">
452+
<title>Planner Statistics and Security</title>
453+
454+
<para>
455+
Access to the table <structname>pg_statistic</structname> is restricted to
456+
superusers, so that ordinary users cannot learn about the contents of the
457+
tables of other users from it. Some selectivity estimation functions will
458+
use a user-provided operator (either the operator appearing in the query or
459+
a related operator) to analyze the stored statistics. For example, in order
460+
to determine whether a stored most common value is applicable, the
461+
selectivity estimator will have to run the appropriate <literal>=</literal>
462+
operator to compare the constant in the query to the stored value.
463+
Thus the data in <structname>pg_statistic</structname> is potentially
464+
passed to user-defined operators. An appropriately crafted operator can
465+
intentionally leak the passed operands (for example, by logging them
466+
or writing them to a different table), or accidentally leak them by showing
467+
their values in error messages, in either case possibly exposing data from
468+
<structname>pg_statistic</structname> to a user who should not be able to
469+
see it.
470+
</para>
471+
472+
<para>
473+
In order to prevent this, the following applies to all built-in selectivity
474+
estimation functions. When planning a query, in order to be able to use
475+
stored statistics, the current user must either
476+
have <literal>SELECT</literal> privilege on the table or the involved
477+
columns, or the operator used must be <literal>LEAKPROOF</literal> (more
478+
accurately, the function that the operator is based on). If not, then the
479+
selectivity estimator will behave as if no statistics are available, and
480+
the planner will proceed with default or fall-back assumptions.
481+
</para>
482+
483+
<para>
484+
If a user does not have the required privilege on the table or columns,
485+
then in many cases the query will ultimately receive a permission-denied
486+
error, in which case this mechanism is invisible in practice. But if the
487+
user is reading from a security-barrier view, then the planner might wish
488+
to check the statistics of an underlying table that is otherwise
489+
inaccessible to the user. In that case, the operator should be leak-proof
490+
or the statistics will not be used. There is no direct feedback about
491+
that, except that the plan might be suboptimal. If one suspects that this
492+
is the case, one could try running the query as a more privileged user,
493+
to see if a different plan results.
494+
</para>
495+
496+
<para>
497+
This restriction applies only to cases where the planner would need to
498+
execute a user-defined operator on one or more values
499+
from <structname>pg_statistic</structname>. Thus the planner is permitted
500+
to use generic statistical information, such as the fraction of null values
501+
or the number of distinct values in a column, regardless of access
502+
privileges.
503+
</para>
504+
505+
<para>
506+
Selectivity estimation functions contained in third-party extensions that
507+
potentially operate on statistics with user-defined operators should follow
508+
the same security rules. Consult the PostgreSQL source code for guidance.
509+
</para>
510+
</sect1>
451511
</chapter>

src/backend/utils/adt/array_selfuncs.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ scalararraysel_containment(PlannerInfo *root,
132132
useOr = !useOr;
133133

134134
/* Get array element stats for var, if available */
135-
if (HeapTupleIsValid(vardata.statsTuple))
135+
if (HeapTupleIsValid(vardata.statsTuple) &&
136+
statistic_proc_security_check(&vardata, cmpfunc->fn_oid))
136137
{
137138
Form_pg_statistic stats;
138139
Datum *values;
@@ -363,7 +364,8 @@ calc_arraycontsel(VariableStatData *vardata, Datum constval,
363364
*/
364365
array = DatumGetArrayTypeP(constval);
365366

366-
if (HeapTupleIsValid(vardata->statsTuple))
367+
if (HeapTupleIsValid(vardata->statsTuple) &&
368+
statistic_proc_security_check(vardata, cmpfunc->fn_oid))
367369
{
368370
Form_pg_statistic stats;
369371
Datum *values;

src/backend/utils/adt/rangetypes_selfuncs.c

+22
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ calc_rangesel(TypeCacheEntry *typcache, VariableStatData *vardata,
255255
if (nnumbers != 1)
256256
elog(ERROR, "invalid empty fraction statistic"); /* shouldn't happen */
257257
empty_frac = numbers[0];
258+
free_attstatsslot(vardata->atttype, NULL, 0, numbers, nnumbers);
258259
}
259260
else
260261
{
@@ -383,6 +384,15 @@ calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata,
383384
bool empty;
384385
double hist_selec;
385386

387+
/* Can't use the histogram with insecure range support functions */
388+
if (!statistic_proc_security_check(vardata,
389+
typcache->rng_cmp_proc_finfo.fn_oid))
390+
return -1;
391+
if (OidIsValid(typcache->rng_subdiff_finfo.fn_oid) &&
392+
!statistic_proc_security_check(vardata,
393+
typcache->rng_subdiff_finfo.fn_oid))
394+
return -1;
395+
386396
/* Try to get histogram of ranges */
387397
if (!(HeapTupleIsValid(vardata->statsTuple) &&
388398
get_attstatsslot(vardata->statsTuple,
@@ -420,11 +430,19 @@ calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata,
420430
NULL,
421431
&length_hist_values, &length_nhist,
422432
NULL, NULL)))
433+
{
434+
free_attstatsslot(vardata->atttype, hist_values, nhist, NULL, 0);
423435
return -1.0;
436+
}
424437

425438
/* check that it's a histogram, not just a dummy entry */
426439
if (length_nhist < 2)
440+
{
441+
free_attstatsslot(vardata->atttype,
442+
length_hist_values, length_nhist, NULL, 0);
443+
free_attstatsslot(vardata->atttype, hist_values, nhist, NULL, 0);
427444
return -1.0;
445+
}
428446
}
429447

430448
/* Extract the bounds of the constant value. */
@@ -560,6 +578,10 @@ calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata,
560578
break;
561579
}
562580

581+
free_attstatsslot(vardata->atttype,
582+
length_hist_values, length_nhist, NULL, 0);
583+
free_attstatsslot(vardata->atttype, hist_values, nhist, NULL, 0);
584+
563585
return hist_selec;
564586
}
565587

0 commit comments

Comments
 (0)