aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2015-10-05 10:06:29 -0400
committerNoah Misch <noah@leadboat.com>2015-10-05 10:06:34 -0400
commit16d58b5b534fa783e2259b407c237fc166ebf7e4 (patch)
tree5e3c003c8ef56fdcb83fe58c17816fb6ba6391ee /src
parent4d95419e8a2006e91a4356b8bb49c1563933f139 (diff)
downloadpostgresql-16d58b5b534fa783e2259b407c237fc166ebf7e4.tar.gz
postgresql-16d58b5b534fa783e2259b407c237fc166ebf7e4.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/json.c6
-rw-r--r--src/test/regress/expected/json.out9
-rw-r--r--src/test/regress/expected/json_1.out9
-rw-r--r--src/test/regress/expected/jsonb.out9
-rw-r--r--src/test/regress/expected/jsonb_1.out9
-rw-r--r--src/test/regress/sql/json.sql6
-rw-r--r--src/test/regress/sql/jsonb.sql6
7 files changed, 54 insertions, 0 deletions
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index d8052eec4de..679315b6587 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -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));
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index bc64512eb10..8bd0fb19692 100644
--- a/src/test/regress/expected/json.out
+++ b/src/test/regress/expected/json.out
@@ -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
diff --git a/src/test/regress/expected/json_1.out b/src/test/regress/expected/json_1.out
index 2738485e5fa..e103f1e8233 100644
--- a/src/test/regress/expected/json_1.out
+++ b/src/test/regress/expected/json_1.out
@@ -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
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index 26dc1bf086b..e4b782270d0 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -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
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
index 3cd65ae9382..7c10d76478b 100644
--- a/src/test/regress/expected/jsonb_1.out
+++ b/src/test/regress/expected/jsonb_1.out
@@ -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
diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql
index ab2dd2ed0d5..ad8f7fe0cc9 100644
--- a/src/test/regress/sql/json.sql
+++ b/src/test/regress/sql/json.sql
@@ -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
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 284b981e500..ecd3f6913d7 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -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