From f8862172e6519b82e66c51baa5b87e29847db2b9 Mon Sep 17 00:00:00 2001 From: Noah Misch Date: Mon, 5 Oct 2015 10:06:29 -0400 Subject: [PATCH] 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 --- src/backend/utils/adt/json.c | 7 +++++++ src/test/regress/expected/json.out | 9 +++++++++ src/test/regress/expected/json_1.out | 9 +++++++++ src/test/regress/sql/json.sql | 6 ++++++ 4 files changed, 31 insertions(+) diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index 0566b926fe..61088a2433 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -21,6 +21,7 @@ #include "lib/stringinfo.h" #include "libpq/pqformat.h" #include "mb/pg_wchar.h" +#include "miscadmin.h" #include "parser/parse_coerce.h" #include "utils/array.h" #include "utils/builtins.h" @@ -427,6 +428,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); @@ -505,6 +508,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); @@ -1338,6 +1343,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result, char *outputstr; text *jsontext; + check_stack_depth(); + if (is_null) { appendStringInfoString(result, "null"); diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out index 0e1d79121f..a43c9e01be 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 f19acf755a..075c3f0e0f 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/sql/json.sql b/src/test/regress/sql/json.sql index 2de144bbaf..f1f898418d 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 -- 2.50.0