Tarantool development patches archive
 help / color / mirror / Atom feed
From: Cyrill Gorcunov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: tml <tarantool-patches@dev.tarantool.org>
Cc: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Subject: [Tarantool-patches] [PATCH v21 1/6] box/func: fix modules functions restore
Date: Thu,  8 Apr 2021 19:41:46 +0300	[thread overview]
Message-ID: <20210408164151.1759348-2-gorcunov@gmail.com> (raw)
In-Reply-To: <20210408164151.1759348-1-gorcunov@gmail.com>

In commit 96938fafb (Add hot function reload for C procedures)
an ability to hot reload of modules has been introduced.
When module is been reloaded his functions are resolved to
new symbols but if something went wrong it is supposed
to restore old symbols from the old module.

Actually current code restores only one function and may
crash if there a bunch of functions to restore. Lets fix it.

Fixes #5968

Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
 changelogs/unreleased/fix-module-reload.md |  8 ++
 src/box/func.c                             | 13 ++-
 test/box/CMakeLists.txt                    |  4 +
 test/box/func_restore.result               | 95 ++++++++++++++++++++++
 test/box/func_restore.test.lua             | 50 ++++++++++++
 test/box/func_restore1.c                   | 33 ++++++++
 test/box/func_restore2.c                   | 27 ++++++
 test/box/func_restore3.c                   | 27 ++++++
 test/box/func_restore4.c                   | 27 ++++++
 9 files changed, 277 insertions(+), 7 deletions(-)
 create mode 100644 changelogs/unreleased/fix-module-reload.md
 create mode 100644 test/box/func_restore.result
 create mode 100644 test/box/func_restore.test.lua
 create mode 100644 test/box/func_restore1.c
 create mode 100644 test/box/func_restore2.c
 create mode 100644 test/box/func_restore3.c
 create mode 100644 test/box/func_restore4.c

diff --git a/changelogs/unreleased/fix-module-reload.md b/changelogs/unreleased/fix-module-reload.md
new file mode 100644
index 000000000..e44118aa8
--- /dev/null
+++ b/changelogs/unreleased/fix-module-reload.md
@@ -0,0 +1,8 @@
+## bugfix/core
+
+* Fix crash in case of reloading a compiled module when the
+  new module lacks some of functions which were present in the
+  former code. In turn this event triggers a fallback procedure
+  where we restore old functions but instead of restoring each
+  function we process a sole entry only leading to the crash
+  later when these restored functions are called (gh-5968).
diff --git a/src/box/func.c b/src/box/func.c
index 9909cee45..94b14c56c 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -387,13 +387,14 @@ module_reload(const char *package, const char *package_end, struct module **modu
 
 	struct func_c *func, *tmp_func;
 	rlist_foreach_entry_safe(func, &old_module->funcs, item, tmp_func) {
+		/* Move immediately for restore sake. */
+		rlist_move(&new_module->funcs, &func->item);
 		struct func_name name;
 		func_split_name(func->base.def->name, &name);
 		func->func = module_sym(new_module, name.sym);
 		if (func->func == NULL)
 			goto restore;
 		func->module = new_module;
-		rlist_move(&new_module->funcs, &func->item);
 	}
 	module_cache_del(package, package_end);
 	if (module_cache_put(new_module) != 0)
@@ -403,10 +404,10 @@ module_reload(const char *package, const char *package_end, struct module **modu
 	return 0;
 restore:
 	/*
-	 * Some old-dso func can't be load from new module, restore old
-	 * functions.
+	 * Some old functions are not found in the new module,
+	 * thus restore all migrated functions back to the original.
 	 */
-	do {
+	rlist_foreach_entry_safe(func, &new_module->funcs, item, tmp_func) {
 		struct func_name name;
 		func_split_name(func->base.def->name, &name);
 		func->func = module_sym(old_module, name.sym);
@@ -420,9 +421,7 @@ module_reload(const char *package, const char *package_end, struct module **modu
 		}
 		func->module = old_module;
 		rlist_move(&old_module->funcs, &func->item);
-	} while (func != rlist_first_entry(&old_module->funcs,
-					   struct func_c, item));
-	assert(rlist_empty(&new_module->funcs));
+	}
 	module_delete(new_module);
 	return -1;
 }
diff --git a/test/box/CMakeLists.txt b/test/box/CMakeLists.txt
index 06bfbbe9d..4216a0ba9 100644
--- a/test/box/CMakeLists.txt
+++ b/test/box/CMakeLists.txt
@@ -2,4 +2,8 @@ include_directories(${MSGPUCK_INCLUDE_DIRS})
 build_module(function1 function1.c)
 build_module(reload1 reload1.c)
 build_module(reload2 reload2.c)
+build_module(func_restore1 func_restore1.c)
+build_module(func_restore2 func_restore2.c)
+build_module(func_restore3 func_restore3.c)
+build_module(func_restore4 func_restore4.c)
 build_module(tuple_bench tuple_bench.c)
diff --git a/test/box/func_restore.result b/test/box/func_restore.result
new file mode 100644
index 000000000..7cd9e67c4
--- /dev/null
+++ b/test/box/func_restore.result
@@ -0,0 +1,95 @@
+-- test-run result file version 2
+--
+-- Test that compiled C function can be restored
+-- to the former values when new module can't be
+-- loaded for some reason (say there some
+-- missing functions).
+--
+build_path = os.getenv("BUILDDIR")
+ | ---
+ | ...
+package.cpath = build_path..'/test/box/?.so;'..build_path..'/test/box/?.dylib;'..package.cpath
+ | ---
+ | ...
+
+fio = require('fio')
+ | ---
+ | ...
+
+ext = (jit.os == "OSX" and "dylib" or "so")
+ | ---
+ | ...
+
+path_func_restore = fio.pathjoin(build_path, "test/box/func_restore.") .. ext
+ | ---
+ | ...
+path_func_good = fio.pathjoin(build_path, "test/box/func_restore1.") .. ext
+ | ---
+ | ...
+
+_ = pcall(fio.unlink(path_func_restore))
+ | ---
+ | ...
+fio.symlink(path_func_good, path_func_restore)
+ | ---
+ | - true
+ | ...
+
+box.schema.func.create('func_restore.echo_1', {language = "C"})
+ | ---
+ | ...
+box.schema.func.create('func_restore.echo_2', {language = "C"})
+ | ---
+ | ...
+box.schema.func.create('func_restore.echo_3', {language = "C"})
+ | ---
+ | ...
+
+box.schema.user.grant('guest', 'execute', 'function', 'func_restore.echo_3')
+ | ---
+ | ...
+box.schema.user.grant('guest', 'execute', 'function', 'func_restore.echo_2')
+ | ---
+ | ...
+box.schema.user.grant('guest', 'execute', 'function', 'func_restore.echo_1')
+ | ---
+ | ...
+
+box.func['func_restore.echo_3']:call()
+ | ---
+ | - 3
+ | ...
+box.func['func_restore.echo_2']:call()
+ | ---
+ | - 2
+ | ...
+box.func['func_restore.echo_1']:call()
+ | ---
+ | - 1
+ | ...
+
+function run_restore(path)                                      \
+    _ = pcall(fio.unlink(path_func_restore))                    \
+    fio.symlink(path, path_func_restore)                        \
+                                                                \
+    local ok, _ = pcall(box.schema.func.reload, "func_restore") \
+    assert(not ok)                                              \
+                                                                \
+    box.func['func_restore.echo_1']:call()                      \
+    box.func['func_restore.echo_2']:call()                      \
+    box.func['func_restore.echo_3']:call()                      \
+end
+ | ---
+ | ...
+
+bad_modules = {                                                 \
+    fio.pathjoin(build_path, "test/box/func_restore2.") .. ext, \
+    fio.pathjoin(build_path, "test/box/func_restore3.") .. ext, \
+    fio.pathjoin(build_path, "test/box/func_restore4.") .. ext, \
+}
+ | ---
+ | ...
+
+for k, v in ipairs(bad_modules) do run_restore(v) end
+ | ---
+ | ...
diff --git a/test/box/func_restore.test.lua b/test/box/func_restore.test.lua
new file mode 100644
index 000000000..4bd05769d
--- /dev/null
+++ b/test/box/func_restore.test.lua
@@ -0,0 +1,50 @@
+--
+-- Test that compiled C function can be restored
+-- to the former values when new module can't be
+-- loaded for some reason (say there some
+-- missing functions).
+--
+build_path = os.getenv("BUILDDIR")
+package.cpath = build_path..'/test/box/?.so;'..build_path..'/test/box/?.dylib;'..package.cpath
+
+fio = require('fio')
+
+ext = (jit.os == "OSX" and "dylib" or "so")
+
+path_func_restore = fio.pathjoin(build_path, "test/box/func_restore.") .. ext
+path_func_good = fio.pathjoin(build_path, "test/box/func_restore1.") .. ext
+
+_ = pcall(fio.unlink(path_func_restore))
+fio.symlink(path_func_good, path_func_restore)
+
+box.schema.func.create('func_restore.echo_1', {language = "C"})
+box.schema.func.create('func_restore.echo_2', {language = "C"})
+box.schema.func.create('func_restore.echo_3', {language = "C"})
+
+box.schema.user.grant('guest', 'execute', 'function', 'func_restore.echo_3')
+box.schema.user.grant('guest', 'execute', 'function', 'func_restore.echo_2')
+box.schema.user.grant('guest', 'execute', 'function', 'func_restore.echo_1')
+
+box.func['func_restore.echo_3']:call()
+box.func['func_restore.echo_2']:call()
+box.func['func_restore.echo_1']:call()
+
+function run_restore(path)                                      \
+    _ = pcall(fio.unlink(path_func_restore))                    \
+    fio.symlink(path, path_func_restore)                        \
+                                                                \
+    local ok, _ = pcall(box.schema.func.reload, "func_restore") \
+    assert(not ok)                                              \
+                                                                \
+    box.func['func_restore.echo_1']:call()                      \
+    box.func['func_restore.echo_2']:call()                      \
+    box.func['func_restore.echo_3']:call()                      \
+end
+
+bad_modules = {                                                 \
+    fio.pathjoin(build_path, "test/box/func_restore2.") .. ext, \
+    fio.pathjoin(build_path, "test/box/func_restore3.") .. ext, \
+    fio.pathjoin(build_path, "test/box/func_restore4.") .. ext, \
+}
+
+for k, v in ipairs(bad_modules) do run_restore(v) end
diff --git a/test/box/func_restore1.c b/test/box/func_restore1.c
new file mode 100644
index 000000000..891ec0136
--- /dev/null
+++ b/test/box/func_restore1.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <msgpuck.h>
+
+#include "module.h"
+
+static int
+echo_num(box_function_ctx_t *ctx, const char *args,
+	 const char *args_end, unsigned int num)
+{
+	char res[16];
+	char *end = mp_encode_uint(res, num);
+	box_return_mp(ctx, res, end);
+	return 0;
+}
+
+int
+echo_1(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 1);
+}
+
+int
+echo_2(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 2);
+}
+
+int
+echo_3(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 3);
+}
diff --git a/test/box/func_restore2.c b/test/box/func_restore2.c
new file mode 100644
index 000000000..d9d6de8df
--- /dev/null
+++ b/test/box/func_restore2.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <msgpuck.h>
+
+#include "module.h"
+
+static int
+echo_num(box_function_ctx_t *ctx, const char *args,
+	 const char *args_end, unsigned int num)
+{
+	char res[16];
+	char *end = mp_encode_uint(res, num);
+	box_return_mp(ctx, res, end);
+	return 0;
+}
+
+int
+echo_1(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 1);
+}
+
+int
+echo_2(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 2);
+}
diff --git a/test/box/func_restore3.c b/test/box/func_restore3.c
new file mode 100644
index 000000000..e38b44400
--- /dev/null
+++ b/test/box/func_restore3.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <msgpuck.h>
+
+#include "module.h"
+
+static int
+echo_num(box_function_ctx_t *ctx, const char *args,
+	 const char *args_end, unsigned int num)
+{
+	char res[16];
+	char *end = mp_encode_uint(res, num);
+	box_return_mp(ctx, res, end);
+	return 0;
+}
+
+int
+echo_2(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 2);
+}
+
+int
+echo_3(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 3);
+}
diff --git a/test/box/func_restore4.c b/test/box/func_restore4.c
new file mode 100644
index 000000000..4e97ff0a0
--- /dev/null
+++ b/test/box/func_restore4.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <msgpuck.h>
+
+#include "module.h"
+
+static int
+echo_num(box_function_ctx_t *ctx, const char *args,
+	 const char *args_end, unsigned int num)
+{
+	char res[16];
+	char *end = mp_encode_uint(res, num);
+	box_return_mp(ctx, res, end);
+	return 0;
+}
+
+int
+echo_1(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 1);
+}
+
+int
+echo_3(box_function_ctx_t *ctx, const char *args, const char *args_end)
+{
+	return echo_num(ctx, args, args_end, 3);
+}
-- 
2.30.2


  reply	other threads:[~2021-04-08 16:42 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-08 16:41 [Tarantool-patches] [PATCH v21 0/6] box: implement box.lib Lua module Cyrill Gorcunov via Tarantool-patches
2021-04-08 16:41 ` Cyrill Gorcunov via Tarantool-patches [this message]
2021-04-09 23:31   ` [Tarantool-patches] [PATCH v21 1/6] box/func: fix modules functions restore Vladislav Shpilevoy via Tarantool-patches
2021-04-10 15:02     ` Cyrill Gorcunov via Tarantool-patches
2021-04-08 16:41 ` [Tarantool-patches] [PATCH v21 2/6] box/func: module_reload -- drop redundant argument Cyrill Gorcunov via Tarantool-patches
2021-04-08 16:41 ` [Tarantool-patches] [PATCH v21 3/6] box/module_cache: introduce modules subsystem Cyrill Gorcunov via Tarantool-patches
2021-04-09 23:54   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-10 14:59     ` Cyrill Gorcunov via Tarantool-patches
2021-04-08 16:41 ` [Tarantool-patches] [PATCH v21 4/6] box/schema.func: switch to new module api Cyrill Gorcunov via Tarantool-patches
2021-04-09 23:55   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-10 15:00     ` Cyrill Gorcunov via Tarantool-patches
2021-04-08 16:41 ` [Tarantool-patches] [PATCH v21 5/6] box: implement box.lib module Cyrill Gorcunov via Tarantool-patches
2021-04-11 15:38   ` Vladislav Shpilevoy via Tarantool-patches
2021-04-11 22:38     ` Cyrill Gorcunov via Tarantool-patches
2021-04-12 22:08       ` Vladislav Shpilevoy via Tarantool-patches
2021-04-12 22:34         ` Cyrill Gorcunov via Tarantool-patches
2021-04-08 16:41 ` [Tarantool-patches] [PATCH v21 6/6] test: add box.lib test Cyrill Gorcunov via Tarantool-patches
2021-04-08 18:53   ` Cyrill Gorcunov via Tarantool-patches
2021-04-11 15:43     ` Vladislav Shpilevoy via Tarantool-patches
2021-04-11 21:56       ` Cyrill Gorcunov via Tarantool-patches
2021-04-11 22:36       ` Cyrill Gorcunov via Tarantool-patches
2021-04-12 22:08         ` Vladislav Shpilevoy via Tarantool-patches
2021-04-13  7:10           ` Cyrill Gorcunov via Tarantool-patches
2021-04-13 21:53 ` [Tarantool-patches] [PATCH v21 0/6] box: implement box.lib Lua module Vladislav Shpilevoy via Tarantool-patches
2021-04-14  8:07 ` 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=20210408164151.1759348-2-gorcunov@gmail.com \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=gorcunov@gmail.com \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH v21 1/6] box/func: fix modules functions restore' \
    /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