Tarantool development patches archive
 help / color / mirror / Atom feed
From: Vladislav Shpilevoy via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: tarantool-patches@dev.tarantool.org, gorcunov@gmail.com,
	sergepetrenko@tarantool.org
Subject: [Tarantool-patches] [PATCH 2/2] swim: check types in __serialize methods
Date: Sun,  4 Apr 2021 17:06:43 +0200	[thread overview]
Message-ID: <a567247516e6b7e33d8abc50ed1bf6aff05d4fe4.1617548717.git.v.shpilevoy@tarantool.org> (raw)
In-Reply-To: <cover.1617548717.git.v.shpilevoy@tarantool.org>

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)


  parent reply	other threads:[~2021-04-04 15:07 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-04 15:06 [Tarantool-patches] [PATCH 0/2] Swim misusage crashes Vladislav Shpilevoy via Tarantool-patches
2021-04-04 15:06 ` [Tarantool-patches] [PATCH 1/2] swim: fix crash on bad member_by_uuid() call Vladislav Shpilevoy via Tarantool-patches
2021-04-04 15:06 ` Vladislav Shpilevoy via Tarantool-patches [this message]
2021-04-04 20:42 ` [Tarantool-patches] [PATCH 0/2] Swim misusage crashes Cyrill Gorcunov via Tarantool-patches
2021-04-05  9:05 ` Serge Petrenko via Tarantool-patches
2021-04-05 15:20 ` Kirill Yukhin via Tarantool-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a567247516e6b7e33d8abc50ed1bf6aff05d4fe4.1617548717.git.v.shpilevoy@tarantool.org \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=gorcunov@gmail.com \
    --cc=sergepetrenko@tarantool.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH 2/2] swim: check types in __serialize methods' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox