diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2017-11-06 10:29:17 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2017-11-06 10:29:41 -0500 |
commit | 70846ee0597b4aabc11ffe252eb972de6f77a021 (patch) | |
tree | 503f3d04d782a35078a5c33f078165ed6dd29260 /src/backend/utils/adt/jsonfuncs.c | |
parent | 29d06705194396d46004a48bf56e741c79269790 (diff) | |
download | postgresql-70846ee0597b4aabc11ffe252eb972de6f77a021.tar.gz postgresql-70846ee0597b4aabc11ffe252eb972de6f77a021.zip |
Make json{b}_populate_recordset() use the right tuple descriptor.
json{b}_populate_recordset() used the tuple descriptor created from the
query-level AS clause without worrying about whether it matched the actual
input record type. If it didn't, that would usually result in a crash,
though disclosure of server memory contents seems possible as well, for a
skilled attacker capable of issuing crafted SQL commands. Instead, use
the query-supplied descriptor only when there is no input tuple to look at,
and otherwise get a tuple descriptor based on the input tuple's own type
marking. The core code will detect any type mismatch in the latter case.
Michael Paquier and Tom Lane, per a report from David Rowley.
Back-patch to 9.3 where this functionality was introduced.
Security: CVE-2017-15098
Diffstat (limited to 'src/backend/utils/adt/jsonfuncs.c')
-rw-r--r-- | src/backend/utils/adt/jsonfuncs.c | 36 |
1 files changed, 25 insertions, 11 deletions
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 18e7b9c8798..3a36a83b3c6 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -2607,26 +2607,37 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname, rsi->returnMode = SFRM_Materialize; - /* - * get the tupdesc from the result set info - it must be a record type - * because we already checked that arg1 is a record type, or we're in a - * to_record function which returns a setof record. - */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("function returning record called in context " - "that cannot accept type record"))); - /* if the json is null send back an empty set */ if (PG_ARGISNULL(json_arg_num)) PG_RETURN_NULL(); if (!have_record_arg || PG_ARGISNULL(0)) + { rec = NULL; + + /* + * get the tupdesc from the result set info - it must be a record type + * because we already checked that arg1 is a record type, or we're in + * a to_record function which returns a setof record. + */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context " + "that cannot accept type record"))); + } else + { rec = PG_GETARG_HEAPTUPLEHEADER(0); + /* + * use the input record's own type marking to find a tupdesc for it. + */ + tupType = HeapTupleHeaderGetTypeId(rec); + tupTypmod = HeapTupleHeaderGetTypMod(rec); + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + } + tupType = tupdesc->tdtypeid; tupTypmod = tupdesc->tdtypmod; ncolumns = tupdesc->natts; @@ -2670,6 +2681,9 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname, false, work_mem); MemoryContextSwitchTo(old_cxt); + /* unnecessary, but harmless, if tupdesc came from get_call_result_type: */ + ReleaseTupleDesc(tupdesc); + state->function_name = funcname; state->my_extra = my_extra; state->rec = rec; |