[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