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

Commit c30f082

Browse files
committed
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
1 parent 6b0b983 commit c30f082

File tree

5 files changed

+66
-11
lines changed

5 files changed

+66
-11
lines changed

src/backend/utils/adt/jsonfuncs.c

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,26 +3491,40 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
34913491

34923492
rsi->returnMode = SFRM_Materialize;
34933493

3494-
/*
3495-
* get the tupdesc from the result set info - it must be a record type
3496-
* because we already checked that arg1 is a record type, or we're in a
3497-
* to_record function which returns a setof record.
3498-
*/
3499-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
3500-
ereport(ERROR,
3501-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3502-
errmsg("function returning record called in context "
3503-
"that cannot accept type record")));
3504-
35053494
/* if the json is null send back an empty set */
35063495
if (PG_ARGISNULL(json_arg_num))
35073496
PG_RETURN_NULL();
35083497

35093498
if (!have_record_arg || PG_ARGISNULL(0))
3499+
{
35103500
rec = NULL;
3501+
3502+
/*
3503+
* get the tupdesc from the result set info - it must be a record type
3504+
* because we already checked that arg1 is a record type, or we're in
3505+
* a to_record function which returns a setof record.
3506+
*/
3507+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
3508+
ereport(ERROR,
3509+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3510+
errmsg("function returning record called in context "
3511+
"that cannot accept type record")));
3512+
}
35113513
else
3514+
{
3515+
Oid tupType;
3516+
int32 tupTypmod;
3517+
35123518
rec = PG_GETARG_HEAPTUPLEHEADER(0);
35133519

3520+
/*
3521+
* use the input record's own type marking to find a tupdesc for it.
3522+
*/
3523+
tupType = HeapTupleHeaderGetTypeId(rec);
3524+
tupTypmod = HeapTupleHeaderGetTypMod(rec);
3525+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
3526+
}
3527+
35143528
state = palloc0(sizeof(PopulateRecordsetState));
35153529

35163530
/* make these in a sufficiently long-lived memory context */
@@ -3522,6 +3536,9 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
35223536
false, work_mem);
35233537
MemoryContextSwitchTo(old_cxt);
35243538

3539+
/* unnecessary, but harmless, if tupdesc came from get_call_result_type: */
3540+
ReleaseTupleDesc(tupdesc);
3541+
35253542
state->function_name = funcname;
35263543
state->my_extra = (RecordIOData **) &fcinfo->flinfo->fn_extra;
35273544
state->rec = rec;

src/test/regress/expected/json.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,6 +1806,19 @@ select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,3
18061806
{"z":true} | 3 | Fri Jan 20 10:42:53 2012
18071807
(2 rows)
18081808

1809+
-- negative cases where the wrong record type is supplied
1810+
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1811+
ERROR: function return row and query-specified return row do not match
1812+
DETAIL: Returned row contains 1 attribute, but query expects 2.
1813+
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1814+
ERROR: function return row and query-specified return row do not match
1815+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
1816+
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1817+
ERROR: function return row and query-specified return row do not match
1818+
DETAIL: Returned row contains 3 attributes, but query expects 2.
1819+
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
1820+
ERROR: function return row and query-specified return row do not match
1821+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
18091822
-- test type info caching in json_populate_record()
18101823
CREATE TEMP TABLE jspoptest (js json);
18111824
INSERT INTO jspoptest

src/test/regress/expected/jsonb.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2488,6 +2488,19 @@ SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":[100,200
24882488
{"z": true} | 3 | Fri Jan 20 10:42:53 2012
24892489
(2 rows)
24902490

2491+
-- negative cases where the wrong record type is supplied
2492+
select * from jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
2493+
ERROR: function return row and query-specified return row do not match
2494+
DETAIL: Returned row contains 1 attribute, but query expects 2.
2495+
select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
2496+
ERROR: function return row and query-specified return row do not match
2497+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
2498+
select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
2499+
ERROR: function return row and query-specified return row do not match
2500+
DETAIL: Returned row contains 3 attributes, but query expects 2.
2501+
select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
2502+
ERROR: function return row and query-specified return row do not match
2503+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
24912504
-- jsonb_to_record and jsonb_to_recordset
24922505
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
24932506
as x(a int, b text, d text);

src/test/regress/sql/json.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,12 @@ select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":
532532
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;
533533
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;
534534

535+
-- negative cases where the wrong record type is supplied
536+
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
537+
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
538+
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
539+
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
540+
535541
-- test type info caching in json_populate_record()
536542
CREATE TEMP TABLE jspoptest (js json);
537543

src/test/regress/sql/jsonb.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,12 @@ SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[{"a":"blurfl","x":43.2},{"b
648648
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;
649649
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;
650650

651+
-- negative cases where the wrong record type is supplied
652+
select * from jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
653+
select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
654+
select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
655+
select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
656+
651657
-- jsonb_to_record and jsonb_to_recordset
652658

653659
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')

0 commit comments

Comments
 (0)