From a62da4eeed042e0ec5c2de0aa4e33b3dd98fc359 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

(cherry picked from commit 19b2cc20df269e31026041c247319aaacf3dd4ff)
---
 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 0e621f6f89..0d0c3c54e1 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 3e09ddcf24..9e3c5c098f 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -119,7 +119,7 @@ struct rlist box_on_shutdown_trigger_list =
 	RLIST_HEAD_INITIALIZER(box_on_shutdown_trigger_list);
 
 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;
 
@@ -4571,7 +4571,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));
@@ -5530,6 +5535,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);
+}
+
 void
 box_free(void)
 {
diff --git a/src/box/box.h b/src/box/box.h
index 9cea1360c8..28825e3877 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -68,7 +68,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
@@ -106,6 +106,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);
+
 /**
  * Cleanup box library
  */
-- 
GitLab