aboutsummaryrefslogtreecommitdiff
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:29 -0400
commit08fa47c4850cea32c3116665975bca219fbf2fe6 (patch)
treec16349140a08476f92c6034d472c67e7e3d3efc2
parent1d812c8b059d0b9b1fba4a459c9876de0f6259b6 (diff)
downloadpostgresql-08fa47c4850cea32c3116665975bca219fbf2fe6.tar.gz
postgresql-08fa47c4850cea32c3116665975bca219fbf2fe6.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
-rw-r--r--src/backend/utils/adt/json.c6
-rw-r--r--src/backend/utils/adt/jsonb.c2
-rw-r--r--src/backend/utils/adt/jsonfuncs.c2
-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
9 files changed, 58 insertions, 0 deletions
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index eefe93bc8ab..f394942bc35 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -490,6 +490,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);
@@ -568,6 +570,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);
@@ -1433,6 +1437,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/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index f0f1651e9da..8fef35ea38a 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -712,6 +712,8 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
JsonbValue jb;
bool scalar_jsonb = false;
+ check_stack_depth();
+
if (is_null)
{
Assert(!key_scalar);
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 01b6bb0a483..f33864e619d 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3724,6 +3724,8 @@ setPath(JsonbIterator **it, Datum *path_elems,
JsonbValue *res = NULL;
int r;
+ check_stack_depth();
+
if (path_nulls[level])
elog(ERROR, "path element at the position %d is NULL", level + 1);
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index eb6b26b241e..cb9a0a1eb30 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 48543e8c356..fe63a72b39c 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 6da5a151d72..148364d74bf 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 39e7b1ff570..903dbd4c402 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 f631480f967..a173f06d958 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 b1a0764cfaa..f86b7fbf9bf 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