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

index 0566b926fedb36dd61b664ed15c2ef976d5eb9df..61088a24339b29d1f7d5f0f1d429362db891ad3a 100644 (file)
@@ -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");
index 0e1d79121f97c0893705e03ab7f642721248c348..a43c9e01bef66dc672f20a15dea8a2ca898ff1df 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 f19acf755a26207cd1b8ff39c2f4b516bf15f90e..075c3f0e0faa9b7f6bc2384afdf8d32ef43e8dd9 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 2de144bbaf7be7e78cfc1ce8cdd8a8e7d6f16583..f1f898418dd33f54d4ba5332a4d04141958902bd 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