1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
local t = require('test.unit.testutil')
local itp = t.gen_itp(it)
local cimport = t.cimport
local eq = t.eq
local neq = t.neq
local ffi = t.ffi
local decode = cimport(
'./src/nvim/eval/decode.h',
'./src/nvim/eval/typval.h',
'./src/nvim/globals.h',
'./src/nvim/memory.h',
'./src/nvim/message.h'
)
describe('json_decode_string()', function()
local char = function(c)
return ffi.gc(decode.xmemdup(c, 1), decode.xfree)
end
itp('does not overflow when running with `n…`, `t…`, `f…`', function()
local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1
-- This will not crash, but if `len` argument will be ignored it will parse
-- `null` as `null` and if not it will parse `null` as `n`.
eq(0, decode.json_decode_string('null', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('true', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('null', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('true', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('null', 3, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('true', 3, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 3, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('false', 4, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
itp('does not overflow and crash when running with `n`, `t`, `f`', function()
local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('n'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string(char('t'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string(char('f'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
itp('does not overflow when running with `"…`', function()
local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1
eq(0, decode.json_decode_string('"t"', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
eq(0, decode.json_decode_string('""', 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
local check_failure = function(s, len, msg)
local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
eq(0, decode.json_decode_string(s, len, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
neq(nil, decode.msg_hist_last)
eq(msg, ffi.string(decode.msg_hist_last.msg.items[0].text.data))
end
itp('does not overflow in error messages', function()
collectgarbage('restart')
check_failure(']test', 1, 'E474: No container to close: ]')
check_failure('[}test', 2, 'E474: Closing list with curly bracket: }')
check_failure('{]test', 2, 'E474: Closing dictionary with square bracket: ]')
check_failure('[1,]test', 4, 'E474: Trailing comma: ]')
check_failure('{"1":}test', 6, 'E474: Expected value after colon: }')
check_failure('{"1"}test', 5, 'E474: Expected value: }')
check_failure(',test', 1, 'E474: Comma not inside container: ,')
check_failure('[1,,1]test', 6, 'E474: Duplicate comma: ,1]')
check_failure('{"1":,}test', 7, 'E474: Comma after colon: ,}')
check_failure('{"1",}test', 6, 'E474: Using comma in place of colon: ,}')
check_failure('{,}test', 3, 'E474: Leading comma: ,}')
check_failure('[,]test', 3, 'E474: Leading comma: ,]')
check_failure(':test', 1, 'E474: Colon not inside container: :')
check_failure('[:]test', 3, 'E474: Using colon not in dictionary: :]')
check_failure('{:}test', 3, 'E474: Unexpected colon: :}')
check_failure('{"1"::1}test', 8, 'E474: Duplicate colon: :1}')
check_failure('ntest', 1, 'E474: Expected null: n')
check_failure('ttest', 1, 'E474: Expected true: t')
check_failure('ftest', 1, 'E474: Expected false: f')
check_failure('"\\test', 2, 'E474: Unfinished escape sequence: "\\')
check_failure('"\\u"test', 4, 'E474: Unfinished unicode escape sequence: "\\u"')
check_failure('"\\uXXXX"est', 8, 'E474: Expected four hex digits after \\u: \\uXXXX"')
check_failure('"\\?"test', 4, 'E474: Unknown escape sequence: \\?"')
check_failure(
'"\t"test',
3,
'E474: ASCII control characters cannot be present inside string: \t"'
)
check_failure('"\194"test', 3, 'E474: Only UTF-8 strings allowed: \194"')
check_failure(
'"\252\144\128\128\128\128"test',
8,
'E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"'
)
check_failure('"test', 1, 'E474: Expected string end: "')
check_failure('-test', 1, 'E474: Missing number after minus sign: -')
check_failure('-1.test', 3, 'E474: Missing number after decimal dot: -1.')
check_failure('-1.0etest', 5, 'E474: Missing exponent: -1.0e')
check_failure('?test', 1, 'E474: Unidentified byte: ?')
check_failure('1?test', 2, 'E474: Trailing characters: ?')
check_failure('[1test', 2, 'E474: Unexpected end of input: [1')
end)
itp('does not overflow with `-`', function()
check_failure('-0', 1, 'E474: Missing number after minus sign: -')
end)
itp('does not overflow and crash when running with `"`', function()
local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('"'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
end)
|