Repository: redis/redis

Issue:
Recent changes in struct clusterMsg break cluster rolling upgrades
@madolson the math doesn't look right.  notused1 should be 30 bytes long as opposed to 16 because uint16_t is 2 bytes (16 bits). This will break cluster rolling upgrades.

_Originally posted by @PingXie in https://github.com/redis/redis/pull/9530#discussion_r794994327_


Discussion:
working on a fix

Patch:
diff --git a/src/ae_evport.c b/src/ae_evport.c
index 9e5b2747669..55393e96b82 100644
--- a/src/ae_evport.c
+++ b/src/ae_evport.c
@@ -28,7 +28,6 @@
  */
 
 
-#include <assert.h>
 #include <errno.h>
 #include <port.h>
 #include <poll.h>
diff --git a/src/cluster.h b/src/cluster.h
index ae63a9d84b1..46565420599 100644
--- a/src/cluster.h
+++ b/src/cluster.h
@@ -321,7 +321,7 @@ typedef struct {
     char slaveof[CLUSTER_NAMELEN];
     char myip[NET_IP_STR_LEN];    /* Sender IP, if not all zeroed. */
     uint16_t extensions; /* Number of extensions sent along with this packet. */
-    char notused1[16];  /* 16 bytes reserved for future usage. */
+    char notused1[30];   /* 30 bytes reserved for future usage. */
     uint16_t pport;      /* Sender TCP plaintext port, if base port is TLS */
     uint16_t cport;      /* Sender TCP cluster bus port */
     uint16_t flags;      /* Sender node flags */
@@ -330,6 +330,40 @@ typedef struct {
     union clusterMsgData data;
 } clusterMsg;
 
+/* clusterMsg defines the gossip wire protocol exchanged among Redis cluster
+ * members, which can be running different versions of redis-server bits,
+ * especially during cluster rolling upgrades.
+ *
+ * Therefore, fields in this struct should remain at the same offset from
+ * release to release. The static asserts below ensures that incompatible
+ * changes in clusterMsg be caught at compile time.
+ */
+
+/* Avoid static_assert on non-C11 compilers. */
+#if __STDC_VERSION__ >= 201112L
+static_assert(offsetof(clusterMsg, sig) == 0, "unexpected field offset");
+static_assert(offsetof(clusterMsg, totlen) == 4, "unexpected field offset");
+static_assert(offsetof(clusterMsg, ver) == 8, "unexpected field offset");
+static_assert(offsetof(clusterMsg, port) == 10, "unexpected field offset");
+static_assert(offsetof(clusterMsg, type) == 12, "unexpected field offset");
+static_assert(offsetof(clusterMsg, count) == 14, "unexpected field offset");
+static_assert(offsetof(clusterMsg, currentEpoch) == 16, "unexpected field offset");
+static_assert(offsetof(clusterMsg, configEpoch) == 24, "unexpected field offset");
+static_assert(offsetof(clusterMsg, offset) == 32, "unexpected field offset");
+static_assert(offsetof(clusterMsg, sender) == 40, "unexpected field offset");
+static_assert(offsetof(clusterMsg, myslots) == 80, "unexpected field offset");
+static_assert(offsetof(clusterMsg, slaveof) == 2128, "unexpected field offset");
+static_assert(offsetof(clusterMsg, myip) == 2168, "unexpected field offset");
+static_assert(offsetof(clusterMsg, extensions) == 2214, "unexpected field offset");
+static_assert(offsetof(clusterMsg, notused1) == 2216, "unexpected field offset");
+static_assert(offsetof(clusterMsg, pport) == 2246, "unexpected field offset");
+static_assert(offsetof(clusterMsg, cport) == 2248, "unexpected field offset");
+static_assert(offsetof(clusterMsg, flags) == 2250, "unexpected field offset");
+static_assert(offsetof(clusterMsg, state) == 2252, "unexpected field offset");
+static_assert(offsetof(clusterMsg, mflags) == 2253, "unexpected field offset");
+static_assert(offsetof(clusterMsg, data) == 2256, "unexpected field offset");
+#endif
+
 #define CLUSTERMSG_MIN_LEN (sizeof(clusterMsg)-sizeof(union clusterMsgData))
 
 /* Message flags better specify the packet content or are used to
diff --git a/src/server.h b/src/server.h
index 06ba543158f..927d6f8596f 100644
--- a/src/server.h
+++ b/src/server.h
@@ -36,6 +36,7 @@
 #include "rio.h"
 #include "atomicvar.h"
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>