From e7a9fd0b255ebf4485feec2841876cb4c667bd28 Mon Sep 17 00:00:00 2001
From: mechanik20051988 <mechanik20051988@tarantool.org>
Date: Fri, 1 Oct 2021 13:09:02 +0300
Subject: [PATCH] iproto: fix crash if box.cfg listen is woken up.

There was access to previously freed memory in case when `cbus_call`
is interrupted: `cbus_call_msg` in iproto allocates on stack, and if
`cbus_call` failed due to fiber cancelation or wake up, `cbus_call_msg`
memory is released. But function called through cbus is still work in
iproto thread and there will be an attempt to access this memory when
this function in iproto thread finished it's work. This patch rework
this behaviour, now before `cbus_call` we reset FIBER_IS_CANCELLABLE
flag, to prevent fiber cancellation or it's wake up.

Closes #6480
---
 ...480-crash-if-box-cfg-listen-is-woken-up.md |  3 ++
 src/box/iproto.cc                             | 32 +++++++++++--------
 ...crash-if-box-cfg-listen-is-woken-up.result | 25 +++++++++++++++
 ...ash-if-box-cfg-listen-is-woken-up.test.lua | 10 ++++++
 4 files changed, 57 insertions(+), 13 deletions(-)
 create mode 100644 changelogs/unreleased/gh-6480-crash-if-box-cfg-listen-is-woken-up.md
 create mode 100644 test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.result
 create mode 100644 test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.test.lua

diff --git a/changelogs/unreleased/gh-6480-crash-if-box-cfg-listen-is-woken-up.md b/changelogs/unreleased/gh-6480-crash-if-box-cfg-listen-is-woken-up.md
new file mode 100644
index 0000000000..cb5cb5bedd
--- /dev/null
+++ b/changelogs/unreleased/gh-6480-crash-if-box-cfg-listen-is-woken-up.md
@@ -0,0 +1,3 @@
+## bugfix/core
+
+* Fixed crash in case a fiber changing box.cfg.listen is woken up (gh-6480).
diff --git a/src/box/iproto.cc b/src/box/iproto.cc
index 436d743e2f..f68f0ea680 100644
--- a/src/box/iproto.cc
+++ b/src/box/iproto.cc
@@ -2955,21 +2955,29 @@ static inline int
 iproto_do_cfg(struct iproto_thread *iproto_thread, struct iproto_cfg_msg *msg)
 {
 	msg->iproto_thread = iproto_thread;
-	if (cbus_call(&iproto_thread->net_pipe, &iproto_thread->tx_pipe, msg,
-		      iproto_do_cfg_f, NULL, TIMEOUT_INFINITY) != 0)
-		return -1;
-	return 0;
+	bool prev = fiber_set_cancellable(false);
+	int rc = cbus_call(&iproto_thread->net_pipe, &iproto_thread->tx_pipe,
+			   msg, iproto_do_cfg_f, NULL, TIMEOUT_INFINITY);
+	fiber_set_cancellable(prev);
+	return rc;
 }
 
-static inline int
+static inline void
+iproto_do_cfg_crit(struct iproto_thread *iproto_thread,
+		   struct iproto_cfg_msg *cfg_msg)
+{
+	int rc = iproto_do_cfg(iproto_thread, cfg_msg);
+	(void)rc;
+	assert(rc == 0);
+}
+
+static inline void
 iproto_send_stop_msg(void)
 {
 	struct iproto_cfg_msg cfg_msg;
 	iproto_cfg_msg_create(&cfg_msg, IPROTO_CFG_STOP);
 	for (int i = 0; i < iproto_threads_count; i++)
-		if (iproto_do_cfg(&iproto_threads[i], &cfg_msg) != 0)
-			return -1;
-	return 0;
+		iproto_do_cfg_crit(&iproto_threads[i], &cfg_msg);
 }
 
 static inline int
@@ -2987,8 +2995,7 @@ iproto_send_listen_msg(struct evio_service *binary)
 int
 iproto_listen(const char *uri)
 {
-	if (iproto_send_stop_msg() != 0)
-		return -1;
+	iproto_send_stop_msg();
 	evio_service_stop(&tx_binary);
 	if (uri == NULL) {
 		tx_binary.addr_len = 0;
@@ -3040,7 +3047,7 @@ iproto_thread_stats_get(struct iproto_stats *stats, int thread_id)
 	iproto_cfg_msg_create(&cfg_msg, IPROTO_CFG_STAT);
 	assert(thread_id >= 0 && thread_id < iproto_threads_count);
 	cfg_msg.stats = stats;
-	iproto_do_cfg(&iproto_threads[thread_id], &cfg_msg);
+	iproto_do_cfg_crit(&iproto_threads[thread_id], &cfg_msg);
 	stats->requests_in_progress =
 		iproto_threads[thread_id].tx.requests_in_progress;
 }
@@ -3066,8 +3073,7 @@ iproto_set_msg_max(int new_iproto_msg_max)
 	iproto_cfg_msg_create(&cfg_msg, IPROTO_CFG_MSG_MAX);
 	cfg_msg.iproto_msg_max = new_iproto_msg_max;
 	for (int i = 0; i < iproto_threads_count; i++) {
-		if (iproto_do_cfg(&iproto_threads[i], &cfg_msg) != 0)
-			diag_raise();
+		iproto_do_cfg_crit(&iproto_threads[i], &cfg_msg);
 		cpipe_set_max_input(&iproto_threads[i].net_pipe,
 				    new_iproto_msg_max / 2);
 	}
diff --git a/test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.result b/test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.result
new file mode 100644
index 0000000000..c942a5b5bb
--- /dev/null
+++ b/test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.result
@@ -0,0 +1,25 @@
+test_run = require('test_run').new()
+---
+...
+fiber = require('fiber')
+---
+...
+f = fiber.create(function() box.stat.net() end) f:wakeup()
+---
+...
+old_listen = box.cfg.listen
+---
+...
+new_listen = old_listen .. "A"
+---
+...
+f = fiber.create(function() box.cfg{listen = new_listen} end) f:wakeup()
+---
+...
+test_run:wait_cond(function() return box.cfg.listen == new_listen end)
+---
+- true
+...
+box.cfg{listen = old_listen}
+---
+...
diff --git a/test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.test.lua b/test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.test.lua
new file mode 100644
index 0000000000..4d52147880
--- /dev/null
+++ b/test/box/gh-6480-crash-if-box-cfg-listen-is-woken-up.test.lua
@@ -0,0 +1,10 @@
+test_run = require('test_run').new()
+fiber = require('fiber')
+
+f = fiber.create(function() box.stat.net() end) f:wakeup()
+
+old_listen = box.cfg.listen
+new_listen = old_listen .. "A"
+f = fiber.create(function() box.cfg{listen = new_listen} end) f:wakeup()
+test_run:wait_cond(function() return box.cfg.listen == new_listen end)
+box.cfg{listen = old_listen}
-- 
GitLab