From 468292dcb743c79714b030557cf2754b7b5bf07d Mon Sep 17 00:00:00 2001 From: LW Date: Fri, 3 Nov 2023 15:56:45 -0700 Subject: fix(rpc): "grid_line" event parsing crashes (#25581) refactor: use a more idiomatic loop to iterate over the cells There are two cases in which the following assertion would fail: ```c assert(g->icell < g->ncells); ``` 1. If `g->ncells = 0`. Update this to be legal. 2. If an EOF is reached while parsing `wrap`. In this case, the unpacker attempts to resume from `cells`, which is a bug. Create a new state for parsing `wrap`. Reference: https://neovim.io/doc/user/ui.html#ui-event-grid_line --- test/unit/msgpack_spec.lua | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 test/unit/msgpack_spec.lua (limited to 'test/unit/msgpack_spec.lua') diff --git a/test/unit/msgpack_spec.lua b/test/unit/msgpack_spec.lua new file mode 100644 index 0000000000..c573714714 --- /dev/null +++ b/test/unit/msgpack_spec.lua @@ -0,0 +1,75 @@ +local helpers = require('test.unit.helpers')(after_each) +local cimport = helpers.cimport +local itp = helpers.gen_itp(it) +local lib = cimport('./src/nvim/msgpack_rpc/unpacker.h', './src/nvim/memory.h') +local ffi = helpers.ffi +local eq = helpers.eq +local to_cstr = helpers.to_cstr + +--- @class Unpacker +--- @field read_ptr ffi.cdata* +--- @field read_size number + +--- @alias Unpacker* table +--- @return Unpacker* unpacker `unpacker[0]` to dereference +local function make_unpacker() + return ffi.gc(ffi.cast('Unpacker*', lib.xcalloc(1, ffi.sizeof('Unpacker'))), function(unpacker) + lib.unpacker_teardown(unpacker, nil, nil) + lib.xfree(unpacker) + end) +end + +--- @param unpacker Unpacker* +--- @param data string +--- @param size number? *default: data:len()* +local function unpacker_goto(unpacker, data, size) + unpacker[0].read_ptr = to_cstr(data) + unpacker[0].read_size = size or data:len() +end + +--- @param unpacker Unpacker* +--- @return boolean +local function unpacker_advance(unpacker) + return lib.unpacker_advance(unpacker) +end + +describe('msgpack', function() + describe('unpacker', function() + itp('does not crash when paused between `cells` and `wrap` params of `grid_line` #25184', function() + -- [kMessageTypeNotification, "redraw", [ + -- ["grid_line", + -- [2, 0, 0, [[" " , 0, 77]], false] + -- ] + -- ]] + local payload = + '\x93\x02\xa6\x72\x65\x64\x72\x61\x77\x91\x92\xa9\x67\x72\x69\x64\x5f\x6c\x69\x6e\x65\x95\x02\x00\x00\x91\x93\xa1\x20\x00\x4d\xc2' + + local unpacker = make_unpacker() + lib.unpacker_init(unpacker) + + unpacker_goto(unpacker, payload, payload:len() - 1) + local finished = unpacker_advance(unpacker) + eq(finished, false) + + unpacker[0].read_size = unpacker[0].read_size + 1 + finished = unpacker_advance(unpacker) + eq(finished, true) + end) + + itp('does not crash when parsing grid_line event with 0 `cells` #25184', function() + local unpacker = make_unpacker() + lib.unpacker_init(unpacker) + + unpacker_goto(unpacker, + -- [kMessageTypeNotification, "redraw", [ + -- ["grid_line", + -- [2, 0, 0, [], false] + -- ] + -- ]] + '\x93\x02\xa6\x72\x65\x64\x72\x61\x77\x91\x92\xa9\x67\x72\x69\x64\x5f\x6c\x69\x6e\x65\x95\x02\x00\x00\x90\xc2' + ) + local finished = unpacker_advance(unpacker) + eq(finished, true) + end) + end) +end) -- cgit