8000 Make json{b}_populate_recordset() use the right tuple descriptor. · jinchung/postgres@70846ee · GitHub
[go: up one dir, main page]

Skip to content

Commit 70846ee

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 29d0670 commit 70846ee

File tree

5 files changed

+63
-11
lines changed

5 files changed

+63
-11
lines changed

src/backend/utils/adt/jsonfuncs.c

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2607,26 +2607,37 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
26072607

26082608
rsi->returnMode = SFRM_Materialize;
26092609

2610-
/*
2611-
* get the tupdesc from the result set info - it must be a record type
2612-
* because we already checked that arg1 is a record type, or we're in a
2613-
* to_record function which returns a setof record.
2614-
*/
2615-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2616-
ereport(ERROR,
2617-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2618-
errmsg("function returning record called in context "
2619-
"that cannot accept type record")));
2620-
26212610
/* if the json is null send back an empty set */
26222611
if (PG_ARGISNULL(json_arg_num))
26232612
PG_RETURN_NULL();
26242613

26252614
if (!have_record_arg || PG_ARGISNULL(0))
2615+
{
26262616
rec = NULL;
2617+
2618+
/*
2619+
* get the tupdesc from the result set info - it must be a record type
2620+
* because we already checked that arg1 is a record type, or we're in
2621+
* a to_record function which returns a setof record.
2622+
*/
2623+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2624+
ereport(ERROR,
2625+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2626+
errmsg("function returning record called in context "
2627+
"that cannot accept type record")));
2628+
}
26272629
else
2630+
{
26282631
rec = PG_GETARG_HEAPTUPLEHEADER(0);
26292632

2633+
/*
2634+
* use the input record's own type marking to find a tupdesc for it.
2635+
*/
2636+
tupType = HeapTupleHeaderGetTypeId(rec);
2637+
tupTypmod = HeapTupleHeaderGetTypMod(rec);
2638+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
2639+
}
2640+
26302641
tupType = tupdesc->tdtypeid;
26312642
tupTypmod = tupdesc->tdtypmod;
26322643
ncolumns = tupdesc->natts;
@@ -2670,6 +2681,9 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
26702681
false, work_mem);
26712682
MemoryContextSwitchTo(old_cxt);
26722683

2684+
/* unnecessary, but harmless, if tupdesc came from get_call_result_type: */
2685+
ReleaseTupleDesc(tupdesc);
2686+
26732687
state->function_name = funcname;
26742688
state->my_extra = my_extra;
26752689
state->rec = rec;

src/test/regress/expected/json.out

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

1387+
-- negative cases where the wrong record type is supplied
1388+
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1389+
ERROR: function return row and query-specified return row do not match
1390+
DETAIL: Returned row contains 1 attribute, but query expects 2.
1391+
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1392+
ERROR: function return row and query-specified return row do not match
1393+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
1394+
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1395+
ERROR: function return row and query-specified return row do not match
1396+
DETAIL: Returned row contains 3 attributes, but query expects 2.
1397+
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
1398+
ERROR: function return row and query-specified return row do not match
1399+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
13871400
--json_typeof() function
13881401
select value, json_typeof(value)
13891402
from (values (json '123.4'),

src/test/regress/expected/jsonb.out

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

1747+
-- negative cases where the wrong record type is supplied
1748+
select * from jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1749+
ERROR: function return row and query-specified return row do not match
1750+
DETAIL: Returned row contains 1 attribute, but query expects 2.
1751+
select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1752+
ERROR: function return row and query-specified return row do not match
1753+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
1754+
select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1755+
ERROR: function return row and query-specified return row do not match
1756+
DETAIL: Returned row contains 3 attributes, but query expects 2.
1757+
select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
1758+
ERROR: function return row and query-specified return row do not match
1759+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
17471760
-- jsonb_to_record and jsonb_to_recordset
17481761
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
17491762
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
@@ -394,6 +394,12 @@ select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":
394394
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;
395395
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;
396396

397+
-- negative cases where the wrong record type is supplied
398+
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
399+
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
400+
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
401+
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
402+
397403
--json_typeof() function
398404
select value, json_typeof(value)
399405
from (values (json '123.4'),

src/test/regress/sql/jsonb.sql

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

367+
-- negative cases where the wrong record type is supplied
368+
select * from jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
369+
select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
370+
select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
371+
select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
372+
367373
-- jsonb_to_record and jsonb_to_recordset
368374

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

0 commit comments

Comments
 (0)
0