mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
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
This commit is contained in:
parent
b500297680
commit
c0c8807ded
@ -1601,19 +1601,47 @@ json_populate_recordset(PG_FUNCTION_ARGS)
|
||||
errmsg("set-valued function called in context that "
|
||||
"cannot accept a set")));
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
(void) get_call_result_type(fcinfo, NULL, &tupdesc);
|
||||
/* if the json is null send back an empty set */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
json = PG_GETARG_TEXT_P(1);
|
||||
|
||||
if (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
|
||||
*/
|
||||
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;
|
||||
|
||||
state = palloc0(sizeof(PopulateRecordsetState));
|
||||
sem = palloc0(sizeof(JsonSemAction));
|
||||
|
||||
|
||||
/* make these in a sufficiently long-lived memory context */
|
||||
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
|
||||
|
||||
@ -1625,20 +1653,8 @@ json_populate_recordset(PG_FUNCTION_ARGS)
|
||||
|
||||
MemoryContextSwitchTo(old_cxt);
|
||||
|
||||
/* if the json is null send back an empty set */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
json = PG_GETARG_TEXT_P(1);
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
rec = NULL;
|
||||
else
|
||||
rec = PG_GETARG_HEAPTUPLEHEADER(0);
|
||||
|
||||
tupType = tupdesc->tdtypeid;
|
||||
tupTypmod = tupdesc->tdtypmod;
|
||||
ncolumns = tupdesc->natts;
|
||||
/* unnecessary, but harmless, if tupdesc came from get_call_result_type: */
|
||||
ReleaseTupleDesc(tupdesc);
|
||||
|
||||
lex = makeJsonLexContext(json, true);
|
||||
|
||||
|
@ -907,3 +907,16 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,3
|
||||
ERROR: cannot call json_populate_recordset on a nested object
|
||||
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
||||
ERROR: cannot call json_populate_recordset on a nested object
|
||||
-- negative cases where the wrong record type is supplied
|
||||
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
ERROR: function return row and query-specified return row do not match
|
||||
DETAIL: Returned row contains 1 attribute, but query expects 2.
|
||||
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
ERROR: function return row and query-specified return row do not match
|
||||
DETAIL: Returned type integer at ordinal position 1, but query expects text.
|
||||
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
ERROR: function return row and query-specified return row do not match
|
||||
DETAIL: Returned row contains 3 attributes, but query expects 2.
|
||||
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
ERROR: function return row and query-specified return row do not match
|
||||
DETAIL: Returned type integer at ordinal position 1, but query expects text.
|
||||
|
@ -302,3 +302,8 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl","
|
||||
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
||||
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
||||
|
||||
-- negative cases where the wrong record type is supplied
|
||||
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
|
||||
|
Loading…
x
Reference in New Issue
Block a user