diff --git a/src/box/func.h b/src/box/func.h
index 30e748dbbeca0f1e8436e8b3f1628328ab5941e5..a8a6932689ab58fe866fa5dfcc929f99a908b1f3 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -90,7 +90,12 @@ void
 func_delete(struct func *func);
 
 /**
- * Call function with arguments represented with given args.
+ * Call function @a func with arguments @a args, put return value to @a ret.
+ * Return 0 on success and nonzero on failure.
+ * The port @a args must be initialized by the caller while @a ret is
+ * initialized by func_call, and only in case of success.
+ * Thus the caller must not initialize @a ret by himself and must destroy it
+ * if and only if func_call returns 0;
  */
 int
 func_call(struct func *func, struct port *args, struct port *ret);
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index defb4cb9d765823453dbc8351e3ef80646ac3d2a..4a01d8d2f2d4c0cfc1fe5153ad1f25e30f5bb9bd 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -83,6 +83,11 @@ ffi.cdef[[
     box_txn_savepoint_t *
     box_txn_savepoint();
 
+    struct port {
+        const struct port_vtab *vtab;
+        char pad[60];
+    };
+
     struct port_c_entry {
         struct port_c_entry *next;
         union {
@@ -1496,7 +1501,8 @@ local iterator_gen_luac = function(param, state) -- luacheck: no unused args
 end
 
 -- global struct port instance to use by select()/get()
-local port_c = ffi.new('struct port_c')
+local port = ffi.new('struct port')
+local port_c = ffi.cast('struct port_c *', port)
 
 -- Helper function to check space:method() usage
 local function check_space_arg(space, method)
@@ -2043,7 +2049,6 @@ base_index_mt.select_ffi = function(index, key, opts)
     local key, key_end = tuple_encode(ibuf, key)
     local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
 
-    local port = ffi.cast('struct port *', port_c)
     local nok = builtin.box_select(index.space_id, index.id, iterator, offset,
                                    limit, key, key_end, port) ~= 0
     cord_ibuf_put(ibuf)
diff --git a/src/box/port.h b/src/box/port.h
index 43d0f9deb7d8d21197777917c5a1674a901385aa..d298e015ebb5598889b204be8416144ca732e9e0 100644
--- a/src/box/port.h
+++ b/src/box/port.h
@@ -124,6 +124,8 @@ struct port_c_entry {
 /**
  * C port is used by C functions from the public API. They can
  * return tuples and arbitrary MessagePack.
+ * Warning: this structure is exposed in FFI, so any change in it must be
+ * replicated if FFI cdef, see schema.lua.
  */
 struct port_c {
 	const struct port_vtab *vtab;
diff --git a/src/lib/core/port.c b/src/lib/core/port.c
index 03694b4668f3b6cb76a2791a1ddf9f7a095412b8..8d5dadbf4cc18cf38baee5bdeaf412eecbb15a8f 100644
--- a/src/lib/core/port.c
+++ b/src/lib/core/port.c
@@ -29,9 +29,11 @@
  * SUCH DAMAGE.
  */
 #include "port.h"
+#include "trivia/util.h"
 
 void
 port_destroy(struct port *port)
 {
 	port->vtab->destroy(port);
+	TRASH(port);
 }
diff --git a/src/lib/core/port.h b/src/lib/core/port.h
index 5c51f76e1141468707609ede3dc026fcec75661e..2f884270980b0c2193f0454614f791943186ba14 100644
--- a/src/lib/core/port.h
+++ b/src/lib/core/port.h
@@ -114,6 +114,8 @@ struct port_vtab {
 /**
  * Abstract port instance. It is supposed to be converted to
  * a concrete port realization, e.g. port_c.
+ * Warning: this structure is exposed in FFI, so any change in it must be
+ * replicated if FFI cdef, see schema.lua.
  */
 struct port {
 	/** Virtual method table. */