[Tarantool-patches] [PATCH 2/2] swim: check types in __serialize methods
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Sun Apr 4 18:06:43 MSK 2021
In swim Lua code none of the __serialize methods checked the
argument type assuming that nobody would call them directly and
mess with the types. But it happened, and is not hard to fix, so
the patch does it.
The serialization functions are sanitized for the swim object,
swim member, and member event.
Closes #5952
---
changelogs/unreleased/swim-serialize-crash.md | 4 +
src/lua/swim.lua | 41 +++++--
test/swim/gh-5952-swim-serialize.result | 101 ++++++++++++++++++
test/swim/gh-5952-swim-serialize.test.lua | 33 ++++++
4 files changed, 169 insertions(+), 10 deletions(-)
create mode 100644 changelogs/unreleased/swim-serialize-crash.md
create mode 100644 test/swim/gh-5952-swim-serialize.result
create mode 100644 test/swim/gh-5952-swim-serialize.test.lua
diff --git a/changelogs/unreleased/swim-serialize-crash.md b/changelogs/unreleased/swim-serialize-crash.md
new file mode 100644
index 000000000..e48b1f518
--- /dev/null
+++ b/changelogs/unreleased/swim-serialize-crash.md
@@ -0,0 +1,4 @@
+## bugfix/swim
+
+* Fix the crash on an attempt to pass an object of a wrong type to `__serialize`
+ method of a swim member in Lua (gh-5952).
diff --git a/src/lua/swim.lua b/src/lua/swim.lua
index f61aa6b58..027f07038 100644
--- a/src/lua/swim.lua
+++ b/src/lua/swim.lua
@@ -322,6 +322,16 @@ local function swim_check_member(m, func_name)
return error(func_name..': first argument is not a SWIM member')
end
+local function swim_check_event(event, func_name)
+ if type(event) == 'table' then
+ local value = event[1]
+ if type(value) == 'number' then
+ return value
+ end
+ end
+ return error(func_name..': first argument is not a SWIM event')
+end
+
--
-- Member methods. Most of them are one-liners, not much to
-- comment.
@@ -435,7 +445,8 @@ local function swim_member_uuid(m)
end
local function swim_member_serialize(m)
- local _, size = swim_member_payload_raw(m.ptr)
+ local ptr = swim_check_member(m, 'member:__serialize()')
+ local _, size = swim_member_payload_raw(ptr)
return {
status = swim_member_status(m),
uuid = swim_member_uuid(m),
@@ -543,6 +554,7 @@ end
-- serialized, for example, in a console.
--
local function swim_serialize(s)
+ swim_check_instance(s, 'swim:__serialize()')
return s.cfg.index
end
@@ -739,31 +751,40 @@ end
local swim_member_event_index = {
is_new = function(self)
- return bit.band(self[1], capi.SWIM_EV_NEW) ~= 0
+ local value = swim_check_event(self, 'event:is_new()')
+ return bit.band(value, capi.SWIM_EV_NEW) ~= 0
end,
is_drop = function(self)
- return bit.band(self[1], capi.SWIM_EV_DROP) ~= 0
+ local value = swim_check_event(self, 'event:is_drop()')
+ return bit.band(value, capi.SWIM_EV_DROP) ~= 0
end,
is_update = function(self)
- return bit.band(self[1], capi.SWIM_EV_UPDATE) ~= 0
+ local value = swim_check_event(self, 'event:is_update()')
+ return bit.band(value, capi.SWIM_EV_UPDATE) ~= 0
end,
is_new_status = function(self)
- return bit.band(self[1], capi.SWIM_EV_NEW_STATUS) ~= 0
+ local value = swim_check_event(self, 'event:is_new_status()')
+ return bit.band(value, capi.SWIM_EV_NEW_STATUS) ~= 0
end,
is_new_uri = function(self)
- return bit.band(self[1], capi.SWIM_EV_NEW_URI) ~= 0
+ local value = swim_check_event(self, 'event:is_new_uri()')
+ return bit.band(value, capi.SWIM_EV_NEW_URI) ~= 0
end,
is_new_incarnation = function(self)
- return bit.band(self[1], capi.SWIM_EV_NEW_INCARNATION) ~= 0
+ local value = swim_check_event(self, 'event:is_new_incarnation()')
+ return bit.band(value, capi.SWIM_EV_NEW_INCARNATION) ~= 0
end,
is_new_generation = function(self)
- return bit.band(self[1], capi.SWIM_EV_NEW_GENERATION) ~= 0
+ local value = swim_check_event(self, 'event:is_new_generation()')
+ return bit.band(value, capi.SWIM_EV_NEW_GENERATION) ~= 0
end,
is_new_version = function(self)
- return bit.band(self[1], capi.SWIM_EV_NEW_VERSION) ~= 0
+ local value = swim_check_event(self, 'event:is_new_version()')
+ return bit.band(value, capi.SWIM_EV_NEW_VERSION) ~= 0
end,
is_new_payload = function(self)
- return bit.band(self[1], capi.SWIM_EV_NEW_PAYLOAD) ~= 0
+ local value = swim_check_event(self, 'event:is_new_payload()')
+ return bit.band(value, capi.SWIM_EV_NEW_PAYLOAD) ~= 0
end,
}
diff --git a/test/swim/gh-5952-swim-serialize.result b/test/swim/gh-5952-swim-serialize.result
new file mode 100644
index 000000000..0f9c3250b
--- /dev/null
+++ b/test/swim/gh-5952-swim-serialize.result
@@ -0,0 +1,101 @@
+-- test-run result file version 2
+fiber = require('fiber')
+ | ---
+ | ...
+swim = require('swim')
+ | ---
+ | ...
+test_run = require('test_run').new()
+ | ---
+ | ...
+test_run:cmd("push filter '\\.lua.*:[0-9]+: ' to '.lua:<line>: '")
+ | ---
+ | - true
+ | ...
+test_run:cmd("push filter '127.0.0.1:[0-9]+$' to '127.0.0.1:<port>'")
+ | ---
+ | - true
+ | ...
+
+--
+-- gh-5952: invalid types in __serialize methods could lead to a crash.
+--
+
+s = swim.new({generation = 0})
+ | ---
+ | ...
+getmetatable(s):__serialize()
+ | ---
+ | - error: 'builtin/swim.lua:<line>: swim:__serialize(): first argument is not a SWIM instance'
+ | ...
+getmetatable(s).__serialize(s)
+ | ---
+ | - []
+ | ...
+
+s:cfg({uuid = uuid(1), uri = uri()})
+ | ---
+ | - true
+ | ...
+getmetatable(s):__serialize()
+ | ---
+ | - error: 'builtin/swim.lua:<line>: swim:__serialize(): first argument is not a SWIM instance'
+ | ...
+getmetatable(s).__serialize(s)
+ | ---
+ | - uuid: 00000000-0000-1000-8000-000000000001
+ | uri: 127.0.0.1:<port>
+ | ...
+
+self = s:self()
+ | ---
+ | ...
+getmetatable(self):__serialize()
+ | ---
+ | - error: 'builtin/swim.lua:<line>: member:__serialize(): first argument is not a SWIM
+ | member'
+ | ...
+getmetatable(self).__serialize(self)
+ | ---
+ | - uri: 127.0.0.1:<port>
+ | status: alive
+ | incarnation: cdata {generation = 0ULL, version = 1ULL}
+ | uuid: 00000000-0000-1000-8000-000000000001
+ | payload_size: 0
+ | ...
+
+event = nil
+ | ---
+ | ...
+_ = s:on_member_event(function(m, e) event = e end)
+ | ---
+ | ...
+s:set_payload(1)
+ | ---
+ | - true
+ | ...
+test_run:wait_cond(function() return event ~= nil end)
+ | ---
+ | - true
+ | ...
+
+getmetatable(event):__serialize()
+ | ---
+ | - error: 'builtin/swim.lua:<line>: event:is_update(): first argument is not a SWIM event'
+ | ...
+getmetatable(event).__serialize(event)
+ | ---
+ | - is_new_payload: true
+ | is_new_version: true
+ | is_new_incarnation: true
+ | is_update: true
+ | ...
+
+s:delete()
+ | ---
+ | ...
+
+test_run:cmd("clear filter")
+ | ---
+ | - true
+ | ...
diff --git a/test/swim/gh-5952-swim-serialize.test.lua b/test/swim/gh-5952-swim-serialize.test.lua
new file mode 100644
index 000000000..7bf75ba4f
--- /dev/null
+++ b/test/swim/gh-5952-swim-serialize.test.lua
@@ -0,0 +1,33 @@
+fiber = require('fiber')
+swim = require('swim')
+test_run = require('test_run').new()
+test_run:cmd("push filter '\\.lua.*:[0-9]+: ' to '.lua:<line>: '")
+test_run:cmd("push filter '127.0.0.1:[0-9]+$' to '127.0.0.1:<port>'")
+
+--
+-- gh-5952: invalid types in __serialize methods could lead to a crash.
+--
+
+s = swim.new({generation = 0})
+getmetatable(s):__serialize()
+getmetatable(s).__serialize(s)
+
+s:cfg({uuid = uuid(1), uri = uri()})
+getmetatable(s):__serialize()
+getmetatable(s).__serialize(s)
+
+self = s:self()
+getmetatable(self):__serialize()
+getmetatable(self).__serialize(self)
+
+event = nil
+_ = s:on_member_event(function(m, e) event = e end)
+s:set_payload(1)
+test_run:wait_cond(function() return event ~= nil end)
+
+getmetatable(event):__serialize()
+getmetatable(event).__serialize(event)
+
+s:delete()
+
+test_run:cmd("clear filter")
--
2.24.3 (Apple Git-128)
More information about the Tarantool-patches
mailing list