Skip to content

Commit 16d58b5

Browse files
committed
Prevent stack overflow in json-related functions.
Sufficiently-deep recursion heretofore elicited a SIGSEGV. If an application constructs PostgreSQL json or jsonb values from arbitrary user input, application users could have exploited this to terminate all active database connections. That applies to 9.3, where the json parser adopted recursive descent, and later versions. Only row_to_json() and array_to_json() were at risk in 9.2, both in a non-security capacity. Back-patch to 9.2, where the json type was introduced. Oskari Saarenmaa, reviewed by Michael Paquier. Security: CVE-2015-5289
1 parent 4d95419 commit 16d58b5

File tree

7 files changed

+54
-0
lines changed

7 files changed

+54
-0
lines changed

src/backend/utils/adt/json.c

+6
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,8 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem)
443443
json_struct_action oend = sem->object_end;
444444
JsonTokenType tok;
445445

446+
check_stack_depth();
447+
446448
if (ostart != NULL)
447449
(*ostart) (sem->semstate);
448450

@@ -521,6 +523,8 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem)
521523
json_struct_action astart = sem->array_start;
522524
json_struct_action aend = sem->array_end;
523525

526+
check_stack_depth();
527+
524528
if (astart != NULL)
525529
(*astart) (sem->semstate);
526530

@@ -1376,6 +1380,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
13761380
char *outputstr;
13771381
text *jsontext;
13781382

1383+
check_stack_depth();
1384+
13791385
/* callers are expected to ensure that null keys are not passed in */
13801386
Assert(!(key_scalar && is_null));
13811387

src/test/regress/expected/json.out

+9
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json;
231231
^
232232
DETAIL: Expected string, but found "3".
233233
CONTEXT: JSON data, line 1: {"abc":1,3...
234+
-- Recursion.
235+
SET max_stack_depth = '100kB';
236+
SELECT repeat('[', 1000)::json;
237+
ERROR: stack depth limit exceeded
238+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
239+
SELECT repeat('{"a":', 1000)::json;
240+
ERROR: stack depth limit exceeded
241+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
242+
RESET max_stack_depth;
234243
-- Miscellaneous stuff.
235244
SELECT 'true'::json; -- OK
236245
json

src/test/regress/expected/json_1.out

+9
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json;
231231
^
232232
DETAIL: Expected string, but found "3".
233233
CONTEXT: JSON data, line 1: {"abc":1,3...
234+
-- Recursion.
235+
SET max_stack_depth = '100kB';
236+
SELECT repeat('[', 1000)::json;
237+
ERROR: stack depth limit exceeded
238+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
239+
SELECT repeat('{"a":', 1000)::json;
240+
ERROR: stack depth limit exceeded
241+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
242+
RESET max_stack_depth;
234243
-- Miscellaneous stuff.
235244
SELECT 'true'::json; -- OK
236245
json

src/test/regress/expected/jsonb.out

+9
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb;
239239
^
240240
DETAIL: Expected string, but found "3".
241241
CONTEXT: JSON data, line 1: {"abc":1,3...
242+
-- Recursion.
243+
SET max_stack_depth = '100kB';
244+
SELECT repeat('[', 1000)::jsonb;
245+
ERROR: stack depth limit exceeded
246+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
247+
SELECT repeat('{"a":', 1000)::jsonb;
248+
ERROR: stack depth limit exceeded
249+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
250+
RESET max_stack_depth;
242251
-- Miscellaneous stuff.
243252
SELECT 'true'::jsonb; -- OK
244253
jsonb

src/test/regress/expected/jsonb_1.out

+9
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb;
239239
^
240240
DETAIL: Expected string, but found "3".
241241
CONTEXT: JSON data, line 1: {"abc":1,3...
242+
-- Recursion.
243+
SET max_stack_depth = '100kB';
244+
SELECT repeat('[', 1000)::jsonb;
245+
ERROR: stack depth limit exceeded
246+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
247+
SELECT repeat('{"a":', 1000)::jsonb;
248+
ERROR: stack depth limit exceeded
249+
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
250+
RESET max_stack_depth;
242251
-- Miscellaneous stuff.
243252
SELECT 'true'::jsonb; -- OK
244253
jsonb

src/test/regress/sql/json.sql

+6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::json; -- OK
4545
SELECT '{"abc":1:2}'::json; -- ERROR, colon in wrong spot
4646
SELECT '{"abc":1,3}'::json; -- ERROR, no value
4747

48+
-- Recursion.
49+
SET max_stack_depth = '100kB';
50+
SELECT repeat('[', 1000)::json;
51+
SELECT repeat('{"a":', 1000)::json;
52+
RESET max_stack_depth;
53+
4854
-- Miscellaneous stuff.
4955
SELECT 'true'::json; -- OK
5056
SELECT 'false'::json; -- OK

src/test/regress/sql/jsonb.sql

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
4848
SELECT '{"abc":1:2}'::jsonb; -- ERROR, colon in wrong spot
4949
SELECT '{"abc":1,3}'::jsonb; -- ERROR, no value
5050

51+
-- Recursion.
52+
SET max_stack_depth = '100kB';
53+
SELECT repeat('[', 1000)::jsonb;
54+
SELECT repeat('{"a":', 1000)::jsonb;
55+
RESET max_stack_depth;
56+
5157
-- Miscellaneous stuff.
5258
SELECT 'true'::jsonb; -- OK
5359
SELECT 'false'::jsonb; -- OK

0 commit comments

Comments
 (0)