From 19b2cc20df269e31026041c247319aaacf3dd4ff Mon Sep 17 00:00:00 2001
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Date: Tue, 9 Jul 2024 22:19:56 +0200
Subject: [PATCH] box: make instance_vclock const

No code besides box.cc can now update instance's vclock
explicitly. That is a protection against hacks like #9916.

Closes #10113

NO_DOC=refactoring
NO_TEST=refactoring
NO_CHANGELOG=refactoring
---
 src/box/applier.cc |  8 ++++++--
 src/box/box.cc     | 17 +++++++++++++++--
 src/box/box.h      | 14 +++++++++++++-
 3 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/src/box/applier.cc b/src/box/applier.cc
index 299c2b9faa..cd16893804 100644
--- a/src/box/applier.cc
+++ b/src/box/applier.cc
@@ -788,7 +788,9 @@ applier_wait_snapshot(struct applier *applier)
 		 * Used to initialize the replica's initial
 		 * vclock in bootstrap_from_master()
 		 */
-		xrow_decode_vclock_xc(&row, instance_vclock);
+		struct vclock vclock;
+		xrow_decode_vclock_xc(&row, &vclock);
+		box_init_instance_vclock(&vclock);
 	}
 
 	coio_read_xrow(io, ibuf, &row);
@@ -844,7 +846,9 @@ applier_wait_snapshot(struct applier *applier)
 				 * vclock yet, do it now. In 1.7+
 				 * this vclock is not used.
 				 */
-				xrow_decode_vclock_xc(&row, instance_vclock);
+				struct vclock vclock;
+				xrow_decode_vclock_xc(&row, &vclock);
+				box_init_instance_vclock(&vclock);
 			}
 			break; /* end of stream */
 		} else if (iproto_type_is_error(row.type)) {
diff --git a/src/box/box.cc b/src/box/box.cc
index 5cabd6b576..2141c50bc5 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -125,7 +125,7 @@ struct rlist box_on_shutdown_trigger_list =
 struct event *box_on_shutdown_event = NULL;
 
 const struct vclock *box_vclock = instance_vclock;
-struct vclock *instance_vclock = &instance_vclock_storage;
+const struct vclock *instance_vclock = &instance_vclock_storage;
 
 const char *box_auth_type;
 
@@ -5107,7 +5107,12 @@ bootstrap_from_master(struct replica *master)
 		return false;
 	}
 	assert(applier->state == APPLIER_READY);
-
+	/*
+	 * In case of rejoin the vclock could be already set to send it in the
+	 * ballot and for other global things. Make it unset again so the
+	 * applier could "init" it again.
+	 */
+	vclock_clear(&instance_vclock_storage);
 	say_info("bootstrapping replica from %s at %s",
 		 tt_uuid_str(&master->uuid),
 		 sio_strfaddr(&applier->addr, applier->addr_len));
@@ -6103,6 +6108,14 @@ box_init(void)
 		       sizeof(struct sync_trigger_data));
 }
 
+void
+box_init_instance_vclock(const struct vclock *vclock)
+{
+	if (vclock_is_set(&instance_vclock_storage))
+		panic("Instance vclock can be initialized only once");
+	vclock_copy(&instance_vclock_storage, vclock);
+}
+
 /** Shutdown box storage i.e. stop parts that need TX loop running. */
 static void
 box_storage_shutdown()
diff --git a/src/box/box.h b/src/box/box.h
index b73f78e7f5..f3a069a7da 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -70,7 +70,7 @@ extern const struct vclock *box_vclock;
  * recovery this vclock points to the end of WAL, not to the current recovery
  * position.
  */
-extern struct vclock *instance_vclock;
+extern const struct vclock *instance_vclock;
 
 /**
  * Name of the authentication method that is currently used on
@@ -118,6 +118,18 @@ extern bool box_is_force_recovery;
 void
 box_init(void);
 
+/**
+ * Set instance's vclock to the given value. Works only if the instance vclock
+ * isn't already set. It must be initialized manually and explicitly this way
+ * when the instance boots from a snapshot - snapshot rows have no LSNs, so
+ * setting the instance vclock manually is the only way.
+ *
+ * It has to be a "public" API of box, because the snapshot can be remote, i.e.
+ * coming from the applier.
+ */
+void
+box_init_instance_vclock(const struct vclock *vclock);
+
 /** Shutdown box storage i.e. stop parts that need TX loop running. */
 void
 box_shutdown(void);
-- 
GitLab