Commit 08fa47c4 authored by Noah Misch's avatar Noah Misch

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
parent 1d812c8b
...@@ -490,6 +490,8 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem) ...@@ -490,6 +490,8 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem)
json_struct_action oend = sem->object_end; json_struct_action oend = sem->object_end;
JsonTokenType tok; JsonTokenType tok;
check_stack_depth();
if (ostart != NULL) if (ostart != NULL)
(*ostart) (sem->semstate); (*ostart) (sem->semstate);
...@@ -568,6 +570,8 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem) ...@@ -568,6 +570,8 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem)
json_struct_action astart = sem->array_start; json_struct_action astart = sem->array_start;
json_struct_action aend = sem->array_end; json_struct_action aend = sem->array_end;
check_stack_depth();
if (astart != NULL) if (astart != NULL)
(*astart) (sem->semstate); (*astart) (sem->semstate);
...@@ -1433,6 +1437,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result, ...@@ -1433,6 +1437,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
char *outputstr; char *outputstr;
text *jsontext; text *jsontext;
check_stack_depth();
/* callers are expected to ensure that null keys are not passed in */ /* callers are expected to ensure that null keys are not passed in */
Assert(!(key_scalar && is_null)); Assert(!(key_scalar && is_null));
......
...@@ -712,6 +712,8 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, ...@@ -712,6 +712,8 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
JsonbValue jb; JsonbValue jb;
bool scalar_jsonb = false; bool scalar_jsonb = false;
check_stack_depth();
if (is_null) if (is_null)
{ {
Assert(!key_scalar); Assert(!key_scalar);
......
...@@ -3724,6 +3724,8 @@ setPath(JsonbIterator **it, Datum *path_elems, ...@@ -3724,6 +3724,8 @@ setPath(JsonbIterator **it, Datum *path_elems,
JsonbValue *res = NULL; JsonbValue *res = NULL;
int r; int r;
check_stack_depth();
if (path_nulls[level]) if (path_nulls[level])
elog(ERROR, "path element at the position %d is NULL", level + 1); elog(ERROR, "path element at the position %d is NULL", level + 1);
......
...@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json; ...@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json;
^ ^
DETAIL: Expected string, but found "3". DETAIL: Expected string, but found "3".
CONTEXT: JSON data, line 1: {"abc":1,3... CONTEXT: JSON data, line 1: {"abc":1,3...
-- Recursion.
SET max_stack_depth = '100kB';
SELECT repeat('[', 1000)::json;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
SELECT repeat('{"a":', 1000)::json;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
RESET max_stack_depth;
-- Miscellaneous stuff. -- Miscellaneous stuff.
SELECT 'true'::json; -- OK SELECT 'true'::json; -- OK
json json
......
...@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json; ...@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json;
^ ^
DETAIL: Expected string, but found "3". DETAIL: Expected string, but found "3".
CONTEXT: JSON data, line 1: {"abc":1,3... CONTEXT: JSON data, line 1: {"abc":1,3...
-- Recursion.
SET max_stack_depth = '100kB';
SELECT repeat('[', 1000)::json;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
SELECT repeat('{"a":', 1000)::json;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
RESET max_stack_depth;
-- Miscellaneous stuff. -- Miscellaneous stuff.
SELECT 'true'::json; -- OK SELECT 'true'::json; -- OK
json json
......
...@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb; ...@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb;
^ ^
DETAIL: Expected string, but found "3". DETAIL: Expected string, but found "3".
CONTEXT: JSON data, line 1: {"abc":1,3... CONTEXT: JSON data, line 1: {"abc":1,3...
-- Recursion.
SET max_stack_depth = '100kB';
SELECT repeat('[', 1000)::jsonb;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
SELECT repeat('{"a":', 1000)::jsonb;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
RESET max_stack_depth;
-- Miscellaneous stuff. -- Miscellaneous stuff.
SELECT 'true'::jsonb; -- OK SELECT 'true'::jsonb; -- OK
jsonb jsonb
......
...@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb; ...@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb;
^ ^
DETAIL: Expected string, but found "3". DETAIL: Expected string, but found "3".
CONTEXT: JSON data, line 1: {"abc":1,3... CONTEXT: JSON data, line 1: {"abc":1,3...
-- Recursion.
SET max_stack_depth = '100kB';
SELECT repeat('[', 1000)::jsonb;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
SELECT repeat('{"a":', 1000)::jsonb;
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth" (currently 100kB), after ensuring the platform's stack depth limit is adequate.
RESET max_stack_depth;
-- Miscellaneous stuff. -- Miscellaneous stuff.
SELECT 'true'::jsonb; -- OK SELECT 'true'::jsonb; -- OK
jsonb jsonb
......
...@@ -45,6 +45,12 @@ SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::json; -- OK ...@@ -45,6 +45,12 @@ SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::json; -- OK
SELECT '{"abc":1:2}'::json; -- ERROR, colon in wrong spot SELECT '{"abc":1:2}'::json; -- ERROR, colon in wrong spot
SELECT '{"abc":1,3}'::json; -- ERROR, no value SELECT '{"abc":1,3}'::json; -- ERROR, no value
-- Recursion.
SET max_stack_depth = '100kB';
SELECT repeat('[', 1000)::json;
SELECT repeat('{"a":', 1000)::json;
RESET max_stack_depth;
-- Miscellaneous stuff. -- Miscellaneous stuff.
SELECT 'true'::json; -- OK SELECT 'true'::json; -- OK
SELECT 'false'::json; -- OK SELECT 'false'::json; -- OK
......
...@@ -48,6 +48,12 @@ SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK ...@@ -48,6 +48,12 @@ SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
SELECT '{"abc":1:2}'::jsonb; -- ERROR, colon in wrong spot SELECT '{"abc":1:2}'::jsonb; -- ERROR, colon in wrong spot
SELECT '{"abc":1,3}'::jsonb; -- ERROR, no value SELECT '{"abc":1,3}'::jsonb; -- ERROR, no value
-- Recursion.
SET max_stack_depth = '100kB';
SELECT repeat('[', 1000)::jsonb;
SELECT repeat('{"a":', 1000)::jsonb;
RESET max_stack_depth;
-- Miscellaneous stuff. -- Miscellaneous stuff.
SELECT 'true'::jsonb; -- OK SELECT 'true'::jsonb; -- OK
SELECT 'false'::jsonb; -- OK SELECT 'false'::jsonb; -- OK
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment