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, NULL, &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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy