Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
To: tarantool-patches@freelists.org, vdavydov.dev@gmail.com
Cc: Kirill Shcherbatov <kshcherbatov@tarantool.org>
Subject: [PATCH v1 5/8] netbox: call persistent functions in netbox
Date: Thu, 30 May 2019 13:45:32 +0300	[thread overview]
Message-ID: <d1fa62b92e67ad0cf4ac09b661d7fc3e07df3334.1559212747.git.kshcherbatov@tarantool.org> (raw)
In-Reply-To: <cover.1559212747.git.kshcherbatov@tarantool.org>

This patch makes persistent Lua functions are available via
net.box.connect() :call method.

Example:
lua_code = [[function(a, b) return a + b end]]
box.schema.func.create('sum', {body = lua_code,
                       opts = {is_deterministic = true}})
conn:call("sum", {1, 3})

Part of #4182
Needed for #1260
---
 src/box/call.c                    |  2 +-
 src/box/lua/call.c                | 36 ++++++++++++-
 src/box/lua/call.h                |  3 +-
 test/box/persistent_func.result   | 84 +++++++++++++++++++++++++++++++
 test/box/persistent_func.test.lua | 36 +++++++++++++
 5 files changed, 157 insertions(+), 4 deletions(-)

diff --git a/src/box/call.c b/src/box/call.c
index 56da53fb3..773b914b1 100644
--- a/src/box/call.c
+++ b/src/box/call.c
@@ -202,7 +202,7 @@ box_process_call(struct call_request *request, struct port *port)
 	if (func && func->def->language == FUNC_LANGUAGE_C) {
 		rc = box_c_call(func, request, port);
 	} else {
-		rc = box_lua_call(request, port);
+		rc = box_lua_call(func, request, port);
 	}
 	/* Restore the original user */
 	if (orig_credentials)
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index c729778c4..2cd982ba8 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -32,6 +32,8 @@
 #include "box/call.h"
 #include "box/error.h"
 #include "fiber.h"
+#include "box/func.h"
+#include "box/schema.h"
 
 #include "lua/utils.h"
 #include "lua/msgpack.h"
@@ -253,6 +255,31 @@ execute_lua_call(lua_State *L)
 	return lua_gettop(L);
 }
 
+static int
+execute_persistent_function(lua_State *L)
+{
+	struct call_request *request = (struct call_request *)
+		lua_topointer(L, 1);
+	lua_settop(L, 0);
+
+	const char *name = request->name;
+	uint32_t name_len = mp_decode_strl(&name);
+	struct func *func = func_by_name(name, name_len);
+	assert(func != NULL);
+	assert(func->lua_func_ref != LUA_REFNIL);
+	lua_rawgeti(L, LUA_REGISTRYINDEX, func->lua_func_ref);
+
+	/* Push the rest of args (a tuple). */
+	const char *args = request->args;
+	uint32_t arg_count = mp_decode_array(&args);
+	luaL_checkstack(L, arg_count, "lua_func: out of stack");
+	for (uint32_t i = 0; i < arg_count; i++)
+		luamp_decode(L, luaL_msgpack_default, &args);
+
+	lua_call(L, arg_count, LUA_MULTRET);
+	return lua_gettop(L);
+}
+
 static int
 execute_lua_eval(lua_State *L)
 {
@@ -396,9 +423,14 @@ box_process_lua(struct call_request *request, struct port *base,
 }
 
 int
-box_lua_call(struct call_request *request, struct port *port)
+box_lua_call(struct func *func, struct call_request *request, struct port *port)
 {
-	return box_process_lua(request, port, execute_lua_call);
+	if (func != NULL && func_def_is_persistent(func->def)) {
+		return box_process_lua(request, port,
+				       execute_persistent_function);
+	} else {
+		return box_process_lua(request, port, execute_lua_call);
+	}
 }
 
 int
diff --git a/src/box/lua/call.h b/src/box/lua/call.h
index 0542123da..06bcfe77e 100644
--- a/src/box/lua/call.h
+++ b/src/box/lua/call.h
@@ -42,13 +42,14 @@ box_lua_call_init(struct lua_State *L);
 
 struct port;
 struct call_request;
+struct func;
 
 /**
  * Invoke a Lua stored procedure from the binary protocol
  * (implementation of 'CALL' command code).
  */
 int
-box_lua_call(struct call_request *request, struct port *port);
+box_lua_call(struct func *func, struct call_request *request, struct port *port);
 
 int
 box_lua_eval(struct call_request *request, struct port *port);
diff --git a/test/box/persistent_func.result b/test/box/persistent_func.result
index 0644de7fe..1b3eaa8b2 100644
--- a/test/box/persistent_func.result
+++ b/test/box/persistent_func.result
@@ -7,6 +7,15 @@ test_run = env.new()
 --
 -- gh-4182: Add persistent LUA functions.
 --
+box.schema.user.grant('guest', 'execute', 'universe')
+---
+...
+net = require('net.box')
+---
+...
+conn = net.connect(box.cfg.listen)
+---
+...
 -- Test valid function.
 test_run:cmd("setopt delimiter ';'")
 ---
@@ -34,9 +43,58 @@ box.schema.func.exists('test')
 ---
 - true
 ...
+conn:call("test", {{address = "Moscow Dolgoprudny"}})
+---
+- [['moscow'], ['dolgoprudny']]
+...
 box.schema.func.create('test2', {body = body, is_deterministic = true})
 ---
 ...
+-- Test that monkey-patch attack is not possible.
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+body_monkey = [[function(tuple)
+	math.abs = math.log
+	return tuple
+end
+]]
+test_run:cmd("setopt delimiter ''");
+---
+...
+box.schema.func.create('body_monkey', {body = body_monkey})
+---
+...
+conn:call("body_monkey", {{address = "Moscow Dolgoprudny"}})
+---
+- {'address': 'Moscow Dolgoprudny'}
+...
+math.abs(-666.666)
+---
+- 666.666
+...
+-- Test taht 'require' is forbidden.
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+body_bad1 = [[function(tuple)
+	local json = require('json')
+	return json.encode(tuple)
+end
+]]
+test_run:cmd("setopt delimiter ''");
+---
+...
+box.schema.func.create('json_serializer', {body = body_bad1})
+---
+...
+conn:call("json_serializer", {{address = "Moscow Dolgoprudny"}})
+---
+- error: '[string "return function(tuple) ..."]:1: attempt to call global ''require''
+    (a nil value)'
+...
 -- Test function with spell error - case 1.
 test_run:cmd("setopt delimiter ';'")
 ---
@@ -71,6 +129,13 @@ box.schema.func.create('body_bad3', {body = body_bad3})
 - error: "Failed to dynamically load function 'body_bad3': func(tuple) \treturn tuple
     end "
 ...
+conn:call("body_bad3", {{address = "Moscow Dolgoprudny"}})
+---
+- error: Procedure 'body_bad3' is not defined
+...
+conn:close()
+---
+...
 -- Restart server.
 test_run:cmd("restart server default")
 net = require('net.box')
@@ -79,6 +144,16 @@ net = require('net.box')
 test_run = require('test_run').new()
 ---
 ...
+conn = net.connect(box.cfg.listen)
+---
+...
+conn:call("test", {{address = "Moscow Dolgoprudny"}})
+---
+- [['moscow'], ['dolgoprudny']]
+...
+conn:close()
+---
+...
 box.schema.func.exists('test')
 ---
 - true
@@ -90,6 +165,15 @@ box.schema.func.exists('test')
 ---
 - false
 ...
+box.schema.func.drop('body_monkey')
+---
+...
+box.schema.func.drop('json_serializer')
+---
+...
 box.schema.func.drop('test2')
 ---
 ...
+box.schema.user.revoke('guest', 'execute', 'universe')
+---
+...
diff --git a/test/box/persistent_func.test.lua b/test/box/persistent_func.test.lua
index 37a761d32..363d687ce 100644
--- a/test/box/persistent_func.test.lua
+++ b/test/box/persistent_func.test.lua
@@ -4,6 +4,10 @@ test_run = env.new()
 --
 -- gh-4182: Add persistent LUA functions.
 --
+box.schema.user.grant('guest', 'execute', 'universe')
+net = require('net.box')
+conn = net.connect(box.cfg.listen)
+
 -- Test valid function.
 test_run:cmd("setopt delimiter ';'")
 body = [[function(tuple)
@@ -17,8 +21,32 @@ test_run:cmd("setopt delimiter ''");
 box.schema.func.create('test', {body = body, language = "C"})
 box.schema.func.create('test', {body = body})
 box.schema.func.exists('test')
+conn:call("test", {{address = "Moscow Dolgoprudny"}})
 box.schema.func.create('test2', {body = body, is_deterministic = true})
 
+-- Test that monkey-patch attack is not possible.
+test_run:cmd("setopt delimiter ';'")
+body_monkey = [[function(tuple)
+	math.abs = math.log
+	return tuple
+end
+]]
+test_run:cmd("setopt delimiter ''");
+box.schema.func.create('body_monkey', {body = body_monkey})
+conn:call("body_monkey", {{address = "Moscow Dolgoprudny"}})
+math.abs(-666.666)
+
+-- Test taht 'require' is forbidden.
+test_run:cmd("setopt delimiter ';'")
+body_bad1 = [[function(tuple)
+	local json = require('json')
+	return json.encode(tuple)
+end
+]]
+test_run:cmd("setopt delimiter ''");
+box.schema.func.create('json_serializer', {body = body_bad1})
+conn:call("json_serializer", {{address = "Moscow Dolgoprudny"}})
+
 -- Test function with spell error - case 1.
 test_run:cmd("setopt delimiter ';'")
 body_bad2 = [[function(tuple)
@@ -36,12 +64,20 @@ end
 ]]
 test_run:cmd("setopt delimiter ''");
 box.schema.func.create('body_bad3', {body = body_bad3})
+conn:call("body_bad3", {{address = "Moscow Dolgoprudny"}})
 
+conn:close()
 -- Restart server.
 test_run:cmd("restart server default")
 net = require('net.box')
 test_run = require('test_run').new()
+conn = net.connect(box.cfg.listen)
+conn:call("test", {{address = "Moscow Dolgoprudny"}})
+conn:close()
 box.schema.func.exists('test')
 box.schema.func.drop('test')
 box.schema.func.exists('test')
+box.schema.func.drop('body_monkey')
+box.schema.func.drop('json_serializer')
 box.schema.func.drop('test2')
+box.schema.user.revoke('guest', 'execute', 'universe')
-- 
2.21.0

  parent reply	other threads:[~2019-05-30 10:45 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-30 10:45 [PATCH v1 0/8] box: functional indexes Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 1/8] box: refactor box_lua_find helper Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 2/8] box: rework func cache update machinery Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 3/8] schema: rework _func system space format Kirill Shcherbatov
2019-05-30 12:06   ` [tarantool-patches] " Konstantin Osipov
2019-06-03 16:14     ` Vladimir Davydov
2019-05-30 13:10   ` Vladislav Shpilevoy
2019-05-30 10:45 ` [PATCH v1 4/8] box: load persistent Lua functions on creation Kirill Shcherbatov
2019-05-31  8:16   ` [tarantool-patches] " Konstantin Osipov
2019-06-03  8:26     ` [tarantool-patches] " Kirill Shcherbatov
2019-05-30 10:45 ` Kirill Shcherbatov [this message]
2019-05-30 10:45 ` [PATCH v1 6/8] box: export _func functions with box.func folder Kirill Shcherbatov
2019-06-03 16:22   ` Vladimir Davydov
2019-06-03 16:24   ` Vladimir Davydov
2019-05-30 10:45 ` [PATCH v1 7/8] box: introduce memtx_slab_alloc helper Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 8/8] box: introduce functional indexes in memtx Kirill Shcherbatov
2019-05-30 11:18 ` [tarantool-patches] [PATCH v1 0/8] box: functional indexes Vladislav Shpilevoy
2019-05-30 11:43   ` [tarantool-patches] " Kirill Shcherbatov
2019-05-30 11:47 ` [tarantool-patches] " Konstantin Osipov
2019-06-03 15:55 ` Vladimir Davydov

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=d1fa62b92e67ad0cf4ac09b661d7fc3e07df3334.1559212747.git.kshcherbatov@tarantool.org \
    --to=kshcherbatov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=vdavydov.dev@gmail.com \
    --subject='Re: [PATCH v1 5/8] netbox: call persistent functions in netbox' \
    /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