mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +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
6b0b983f79
commit
c30f082d27
@ -3491,26 +3491,40 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
|
|||||||
|
|
||||||
rsi->returnMode = SFRM_Materialize;
|
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 the json is null send back an empty set */
|
||||||
if (PG_ARGISNULL(json_arg_num))
|
if (PG_ARGISNULL(json_arg_num))
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
if (!have_record_arg || PG_ARGISNULL(0))
|
if (!have_record_arg || PG_ARGISNULL(0))
|
||||||
|
{
|
||||||
rec = NULL;
|
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
|
else
|
||||||
|
{
|
||||||
|
Oid tupType;
|
||||||
|
int32 tupTypmod;
|
||||||
|
|
||||||
rec = PG_GETARG_HEAPTUPLEHEADER(0);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
state = palloc0(sizeof(PopulateRecordsetState));
|
state = palloc0(sizeof(PopulateRecordsetState));
|
||||||
|
|
||||||
/* make these in a sufficiently long-lived memory context */
|
/* make these in a sufficiently long-lived memory context */
|
||||||
@ -3522,6 +3536,9 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
|
|||||||
false, work_mem);
|
false, work_mem);
|
||||||
MemoryContextSwitchTo(old_cxt);
|
MemoryContextSwitchTo(old_cxt);
|
||||||
|
|
||||||
|
/* unnecessary, but harmless, if tupdesc came from get_call_result_type: */
|
||||||
|
ReleaseTupleDesc(tupdesc);
|
||||||
|
|
||||||
state->function_name = funcname;
|
state->function_name = funcname;
|
||||||
state->my_extra = (RecordIOData **) &fcinfo->flinfo->fn_extra;
|
state->my_extra = (RecordIOData **) &fcinfo->flinfo->fn_extra;
|
||||||
state->rec = rec;
|
state->rec = rec;
|
||||||
|
@ -1806,6 +1806,19 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,3
|
|||||||
{"z":true} | 3 | Fri Jan 20 10:42:53 2012
|
{"z":true} | 3 | Fri Jan 20 10:42:53 2012
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
-- 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.
|
||||||
-- test type info caching in json_populate_record()
|
-- test type info caching in json_populate_record()
|
||||||
CREATE TEMP TABLE jspoptest (js json);
|
CREATE TEMP TABLE jspoptest (js json);
|
||||||
INSERT INTO jspoptest
|
INSERT INTO jspoptest
|
||||||
|
@ -2488,6 +2488,19 @@ SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":[100,200
|
|||||||
{"z": true} | 3 | Fri Jan 20 10:42:53 2012
|
{"z": true} | 3 | Fri Jan 20 10:42:53 2012
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
-- negative cases where the wrong record type is supplied
|
||||||
|
select * from jsonb_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 jsonb_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 jsonb_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 jsonb_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.
|
||||||
-- jsonb_to_record and jsonb_to_recordset
|
-- jsonb_to_record and jsonb_to_recordset
|
||||||
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
|
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
|
||||||
as x(a int, b text, d text);
|
as x(a int, b text, d text);
|
||||||
|
@ -532,6 +532,12 @@ select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":
|
|||||||
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
||||||
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,'[{"a":[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);
|
||||||
|
|
||||||
-- test type info caching in json_populate_record()
|
-- test type info caching in json_populate_record()
|
||||||
CREATE TEMP TABLE jspoptest (js json);
|
CREATE TEMP TABLE jspoptest (js json);
|
||||||
|
|
||||||
|
@ -648,6 +648,12 @@ SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[{"a":"blurfl","x":43.2},{"b
|
|||||||
SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
||||||
SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
|
SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":[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 jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||||
|
select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||||
|
select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
|
||||||
|
select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
|
||||||
|
|
||||||
-- jsonb_to_record and jsonb_to_recordset
|
-- jsonb_to_record and jsonb_to_recordset
|
||||||
|
|
||||||
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
|
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user