10000 Make json{b}_populate_recordset() use the right tuple descriptor. · PizzaL/postgres@38e8256 · GitHub
[go: up one dir, main page]

Skip to content

Commit 38e8256

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 b7d6f75 commit 38e8256

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
@@ -2696,26 +2696,37 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
26962696

26972697
rsi->returnMode = SFRM_Materialize;
26982698

2699-
/*
2700-
* get the tupdesc from the result set info - it must be a record type
2701-
* because we already checked that arg1 is a record type, or we're in a
2702-
* to_record function which returns a setof record.
2703-
*/
2704-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2705-
ereport(ERROR,
2706-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2707-
errmsg("function returning record called in context "
2708-
"that cannot accept type record")));
2709-
27102699
/* if the json is null send back an empty set */
27112700
if (PG_ARGISNULL(json_arg_num))
27122701
PG_RETURN_NULL();
27132702

27142703
if (!have_record_arg || PG_ARGISNULL(0))
2704+
{
27152705
rec = NULL;
2706+
2707+
/*
2708+
* get the tupdesc from the result set info - it must be a record type
2709+
* because we already checked that arg1 is a record type, or we're in
2710+
* a to_record function which returns a setof record.
2711+
*/
2712+
if (get_call_result_type(fcinfo, NU 10000 LL, &tupdesc) != TYPEFUNC_COMPOSITE)
2713+
ereport(ERROR,
2714+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2715+
errmsg("function returning record called in context "
2716+
"that cannot accept type record")));
2717+
}
27162718
else
2719+
{
27172720
rec = PG_GETARG_HEAPTUPLEHEADER(0);
27182721

2722+
/*
2723+
* use the input record's own type marking to find a tupdesc for it.
2724+
*/
2725+
tupType = HeapTupleHeaderGetTypeId(rec);
2726+
tupTypmod = HeapTupleHeaderGetTypMod(rec);
2727+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
2728+
}
2729+
27192730
tupType = tupdesc->tdtypeid;
27202731
tupTypmod = tupdesc->tdtypmod;
27212732
ncolumns = tupdesc->natts;
@@ -2759,6 +2770,9 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
27592770
false, work_mem);
27602771
MemoryContextSwitchTo(old_cxt);
27612772

2773+
/* unnecessary, but harmless, if tupdesc came from get_call_result_type: */
2774+
ReleaseTupleDesc(tupdesc);
2775+
27622776
state->function_name = funcname;
27632777
state->my_extra = my_extra;
27642778
state->rec = rec;

src/test/regress/expected/json.out

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

1411+
-- negative cases where the wrong record type is supplied
1412+
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1413+
ERROR: function return row and query-specified return row do not match
1414+
DETAIL: Returned row contains 1 attribute, but query expects 2.
1415+
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1416+
ERROR: function return row and query-specified return row do not match
1417+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
1418+
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
1419+
ERROR: function return row and query-specified return row do not match
1420+
DETAIL: Returned row contains 3 attributes, but query expects 2.
1421+
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
1422+
ERROR: function return row and query-specified return row do not match
1423+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
14111424
--json_typeof() function
14121425
select value, json_typeof(value)
14131426
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
@@ -2090,6 +2090,19 @@ SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":[100,200
20902090
{"z": true} | 3 | Fri Jan 20 10:42:53 2012
20912091
(2 rows)
20922092

2093+
-- negative cases where the wrong record type is supplied
2094+
select * from jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
2095+
ERROR: function return row and query-specified return row do not match
2096+
DETAIL: Returned row contains 1 attribute, but query expects 2.
2097+
select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
2098+
ERROR: function return row and query-specified return row do not match
2099+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
2100+
select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
2101+
ERROR: function return row and query-specified return row do not match
2102+
DETAIL: Returned row contains 3 attributes, but query expects 2.
2103+
select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
2104+
ERROR: function return row and query-specified return row do not match
2105+
DETAIL: Returned type integer at ordinal position 1, but query expects text.
20932106
-- jsonb_to_record and jsonb_to_recordset
20942107
select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}')
20952108
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
@@ -404,6 +404,12 @@ select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":
404404
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;
405405
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;
406406

407+
-- negative cases where the wrong record type is supplied
408+
select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
409+
select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
410+
select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
411+
select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
412+
407413
--json_typeof() function
408414
select value, json_typeof(value)
409415
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
@@ -520,6 +520,12 @@ SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[{"a":"blurfl","x":43.2},{"b
520520
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;
521521
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;
522522

523+
-- negative cases where the wrong record type is supplied
524+
select * from jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
525+
select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
526+
select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
527+
select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
528+
523529
-- jsonb_to_record and jsonb_to_recordset
524530

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

0 commit comments

Comments
 (0)
0