Skip to content
Snippets Groups Projects
Unverified Commit 9e403e42 authored by Olga Arkhangelskaia's avatar Olga Arkhangelskaia Committed by Alexander Turenko
Browse files

json: don't spoil instance with per-call options

When json.decode is used with 2 arguments, 2nd argument seeps out to the
json configuration of the instance. Moreover, due to current
serializer.cfg implementation it remains invisible while checking
settings using json.cfg table.

This fixes commit 6508ddb7 ('json: fix
stack-use-after-scope in json_decode()').

Closes #4761

(cherry picked from commit f54f4dc0)
parent 7a7157f6
No related merge requests found
#!/usr/bin/env tarantool
local json = require('json')
local tap = require('tap')
--
-- gh-4761: json.decode silently changes instance settings when
-- called with 2nd parameter.
--
-- Verify json.encode as well.
--
local res = tap.test('gh-4761-json-per-call-options', function(test)
test:plan(2)
-- Preparation code: call :decode() with a custom option.
local ok, err = pcall(json.decode, '{"foo": {"bar": 1}}',
{decode_max_depth = 1})
assert(not ok, 'expect "too many nested data structures" error')
-- Verify that the instance option remains unchanged.
local exp_res = {foo = {bar = 1}}
local ok, res = pcall(json.decode, '{"foo": {"bar": 1}}')
test:is_deeply({ok, res}, {true, exp_res},
'json instance settings remain unchanged after :decode()')
-- Same check for json.encode.
local nan = 1/0
local ok, err = pcall(json.encode, {a = nan},
{encode_invalid_numbers = false})
assert(not ok, 'expected "number must not be NaN or Inf" error')
local exp_res = '{"a":inf}'
local ok, res = pcall(json.encode, {a = nan})
test:is_deeply({ok, res}, {true, exp_res},
'json instance settings remain unchanged after :encode()')
end)
os.exit(res and 0 or 1)
......@@ -998,13 +998,27 @@ static int json_decode(lua_State *l)
luaL_argcheck(l, lua_gettop(l) == 2 || lua_gettop(l) == 1, 1,
"expected 1 or 2 arguments");
struct luaL_serializer *cfg = luaL_checkserializer(l);
/*
* user_cfg is per-call local version of serializer instance
* options: it is used if a user passes custom options to
* :decode() method within a separate argument. In this case
* it is required to avoid modifying options of the instance.
* Life span of user_cfg is restricted by the scope of
* :decode() so it is enough to allocate it on the stack.
*/
struct luaL_serializer user_cfg;
json.cfg = cfg;
if (lua_gettop(l) == 2) {
struct luaL_serializer *user_cfg = luaL_checkserializer(l);
luaL_serializer_parse_options(l, user_cfg);
/*
* on_update triggers are left uninitialized for user_cfg.
* The decoding code don't (and shouldn't) run them.
*/
luaL_serializer_copy_options(&user_cfg, cfg);
luaL_serializer_parse_options(l, &user_cfg);
lua_pop(l, 1);
json.cfg = user_cfg;
} else {
json.cfg = luaL_checkserializer(l);
json.cfg = &user_cfg;
}
json.data = luaL_checklstring(l, 1, &json_len);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment