Prevent stack overflow in json-related functions.
authorNoah Misch
Mon, 5 Oct 2015 14:06:29 +0000 (10:06 -0400)
committerNoah Misch
Mon, 5 Oct 2015 14:06:34 +0000 (10:06 -0400)
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

src/backend/utils/adt/json.c
src/test/regress/expected/json.out
src/test/regress/expected/json_1.out
src/test/regress/expected/jsonb.out
src/test/regress/expected/jsonb_1.out
src/test/regress/sql/json.sql
src/test/regress/sql/jsonb.sql

index d8052eec4dec63ac106a697dd5abb6e15dfcf893..679315b65871a5f20d082f4674e802c5aa315282 100644 (file)
@@ -443,6 +443,8 @@ parse_object(JsonLexContext *lex, JsonSemAction *sem)
    json_struct_action oend = sem->object_end;
    JsonTokenType tok;
 
+   check_stack_depth();
+
    if (ostart != NULL)
        (*ostart) (sem->semstate);
 
@@ -521,6 +523,8 @@ parse_array(JsonLexContext *lex, JsonSemAction *sem)
    json_struct_action astart = sem->array_start;
    json_struct_action aend = sem->array_end;
 
+   check_stack_depth();
+
    if (astart != NULL)
        (*astart) (sem->semstate);
 
@@ -1376,6 +1380,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
    char       *outputstr;
    text       *jsontext;
 
+   check_stack_depth();
+
    /* callers are expected to ensure that null keys are not passed in */
    Assert(!(key_scalar && is_null));
 
index bc64512eb10ecc2af889fd0bbc43d1c63601d8bc..8bd0fb196924e378073cdc6e544de024c17c02af 100644 (file)
@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json;
                ^
 DETAIL:  Expected string, but found "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.
 SELECT 'true'::json;           -- OK
  json 
index 2738485e5fa12e56db46548f8ff273dcf57cb6cc..e103f1e82331f74b8f414bcf61854d09feb1c23c 100644 (file)
@@ -231,6 +231,15 @@ LINE 1: SELECT '{"abc":1,3}'::json;
                ^
 DETAIL:  Expected string, but found "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.
 SELECT 'true'::json;           -- OK
  json 
index 26dc1bf086b6c92357bf84899962dd0092acfeb7..e4b782270d0d1978998f635ac0efa3d6df3859e1 100644 (file)
@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb;
                ^
 DETAIL:  Expected string, but found "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.
 SELECT 'true'::jsonb;          -- OK
  jsonb 
index 3cd65ae9382ea7f23bac00b9a77baad6e09f0028..7c10d76478bb8e76a3cf08397f2aace9aee818a8 100644 (file)
@@ -239,6 +239,15 @@ LINE 1: SELECT '{"abc":1,3}'::jsonb;
                ^
 DETAIL:  Expected string, but found "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.
 SELECT 'true'::jsonb;          -- OK
  jsonb 
index ab2dd2ed0d5315b5c52119b19ac0d5c383caafce..ad8f7fe0cc9da49aac5dee1f65407cd1ac38e53b 100644 (file)
@@ -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,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.
 SELECT 'true'::json;           -- OK
 SELECT 'false'::json;          -- OK
index 284b981e5001a325bfb304b35f1ee2a47e1b9608..ecd3f6913d704a97dc886c2edc1339332c4d6ea5 100644 (file)
@@ -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,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.
 SELECT 'true'::jsonb;          -- OK
 SELECT 'false'::jsonb;         -- OK