Repository: redis/redis

Problem statement:
[BUG] EVAL "return _G" 0  leads to immediate panic
**Describe the bug**

redis-server crashes with message:

`PANIC: unprotected error in call to Lua API (Script attempted to access nonexistent global variable 'err')`

**To reproduce**

```
$ redis-cli -h localhost
localhost:6379> EVAL "return _G" 0
Error: Server closed the connection
```

**Expected behavior**

Server just returns error

**Additional information**

The problem reproduced on version 6.2 and 7.0.4 on x86_64



Hints:

Patch:
diff --git a/.github/ISSUE_TEMPLATE/crash_report.md b/.github/ISSUE_TEMPLATE/crash_report.md
index c608ccdc142..5b1fa581175 100644
--- a/.github/ISSUE_TEMPLATE/crash_report.md
+++ b/.github/ISSUE_TEMPLATE/crash_report.md
@@ -1,12 +1,17 @@
 ---
 name: Crash report
 about: Submit a crash report
-title: '[CRASH]'
+title: '[CRASH] <short description>'
 labels: ''
 assignees: ''
 
 ---
 
+Notice!
+- If a Redis module was involved, please open an issue in the module's repo instead!
+- If you're using docker on Apple M1, please make sure the image you're using was compiled for ARM!
+
+
 **Crash report**
 
 Paste the complete crash log between the quotes below. Please include a few lines from the log preceding the crash report to provide some context.
diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index 68e5711c8cf..de8a061b354 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -247,13 +247,13 @@ jobs:
       run: ./runtest-cluster
 
   test-freebsd:
-    runs-on: macos-10.15
+    runs-on: macos-12
     if: github.repository == 'redis/redis'
     timeout-minutes: 14400
     steps:
     - uses: actions/checkout@v2
     - name: test
-      uses: vmactions/freebsd-vm@v0.1.6
+      uses: vmactions/freebsd-vm@v0.3.0
       with:
         usesh: true
         sync: rsync
diff --git a/00-RELEASENOTES b/00-RELEASENOTES
index a5557e6dfb7..9aa29f3a07b 100644
--- a/00-RELEASENOTES
+++ b/00-RELEASENOTES
@@ -11,6 +11,45 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
 SECURITY: There are security fixes in the release.
 --------------------------------------------------------------------------------
 
+================================================================================
+Redis 6.2.8 Released Mon 12 Dec 2022 12:00:00 IST
+================================================================================
+
+Upgrade urgency: MODERATE, Contains fixes for a few non-critical or unlikely bugs
+
+Performance and resource utilization improvements
+=================================================
+
+* Optimize zset conversion on large ZRANGESTORE (#10789)
+
+Module API changes
+==================
+
+* Fix crash in CLIENT_CHANGE event, when the selected database is not 0 (#11500)
+* Fix RM_SetAbsExpire and RM_GetAbsExpire API registration (#11025, #8564)
+
+Security improvements
+=====================
+
+* Sentinel: avoid logging auth-pass value (#9652)
+
+Bug Fixes
+=========
+
+* Fix a crash when a Lua script returns a meta-table (#11032)
+* Fix ZRANGESTORE crash when zset_max_listpack_entries is 0 (#10767)
+* Unpause clients after manual failover ends instead of waiting for timed (#9676)
+* TLS: Notify clients on connection shutdown (#10931)
+* Avoid hang of diskless replication fork child when parent crashes (#11463)
+* Fix sentinel function that compares hostnames if failed resolve (#11419)
+* Fix a hang when eviction is combined with lazy-free and maxmemory-eviction-tenacity
+  is set to 100 (#11237)
+* Fix bug with scripts ignoring client tracking NOLOOP (#11052)
+* Fix client-side tracking breaking protocol when FLUSHDB / FLUSHALL / SWAPDB is
+  used inside MULTI-EXEC (#11038)
+* Fix BITFIELD overflow detection on some compilers due to undefined behaviour (#9601)
+
+
 ================================================================================
 Redis 6.2.7 Released Wed Apr 27 12:00:00 IDT 2022
 ================================================================================
diff --git a/src/bitops.c b/src/bitops.c
index f1c563a412f..841f6d64438 100644
--- a/src/bitops.c
+++ b/src/bitops.c
@@ -328,8 +328,9 @@ int checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int
 
     /* Note that maxincr and minincr could overflow, but we use the values
      * only after checking 'value' range, so when we use it no overflow
-     * happens. */
-    int64_t maxincr = max-value;
+     * happens. 'uint64_t' cast is there just to prevent undefined behavior on
+     * overflow */
+    int64_t maxincr = (uint64_t)max-value;
     int64_t minincr = min-value;
 
     if (value > max || (bits != 64 && incr > maxincr) || (value >= 0 && incr > 0 && incr > maxincr))
diff --git a/src/childinfo.c b/src/childinfo.c
index 5e94a4f2ca8..b9f5b3b4ac3 100644
--- a/src/childinfo.c
+++ b/src/childinfo.c
@@ -106,7 +106,9 @@ void sendChildInfoGeneric(childInfoType info_type, size_t keys, double progress,
     ssize_t wlen = sizeof(data);
 
     if (write(server.child_info_pipe[1], &data, wlen) != wlen) {
-        /* Nothing to do on error, this will be detected by the other side. */
+        /* Failed writing to parent, it could have been killed, exit. */
+        serverLog(LL_WARNING,"Child failed reporting info to parent, exiting. %s", strerror(errno));
+        exitFromChild(1);
     }
 }
 
diff --git a/src/cluster.c b/src/cluster.c
index 88626d94a2a..38bafa8fffe 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -554,6 +554,7 @@ void clusterInit(void) {
     deriveAnnouncedPorts(&myself->port, &myself->pport, &myself->cport);
 
     server.cluster->mf_end = 0;
+    server.cluster->mf_slave = NULL;
     resetManualFailover();
     clusterUpdateMyselfFlags();
 }
@@ -3451,8 +3452,10 @@ void clusterHandleSlaveMigration(int max_slaves) {
  * The function can be used both to initialize the manual failover state at
  * startup or to abort a manual failover in progress. */
 void resetManualFailover(void) {
-    if (server.cluster->mf_end) {
-        checkClientPauseTimeoutAndReturnIfPaused();
+    if (server.cluster->mf_slave) {
+        /* We were a master failing over, so we paused clients. Regardless
+         * of the outcome we unpause now to allow traffic again. */
+        unpauseClients();
     }
     server.cluster->mf_end = 0; /* No manual failover in progress. */
     server.cluster->mf_can_start = 0;
diff --git a/src/config.c b/src/config.c
index e0eec2f6752..691b8a045eb 100644
--- a/src/config.c
+++ b/src/config.c
@@ -388,6 +388,7 @@ void initConfigValues() {
 }
 
 void loadServerConfigFromString(char *config) {
+    char buf[1024];
     const char *err = NULL;
     int linenum = 0, totlines, i;
     int slaveof_linenum = 0;
@@ -588,7 +589,6 @@ void loadServerConfigFromString(char *config) {
         } else if (!strcasecmp(argv[0],"user") && argc >= 2) {
             int argc_err;
             if (ACLAppendUserForLoading(argv,argc,&argc_err) == C_ERR) {
-                char buf[1024];
                 const char *errmsg = ACLSetUserStringError();
                 snprintf(buf,sizeof(buf),"Error in user declaration '%s': %s",
                     argv[argc_err],errmsg);
diff --git a/src/debug.c b/src/debug.c
index 71ef51f8be3..5318c1469eb 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1019,61 +1019,75 @@ void bugReportStart(void) {
 }
 
 #ifdef HAVE_BACKTRACE
-static void *getMcontextEip(ucontext_t *uc) {
+
+/* Returns the current eip and set it to the given new value (if its not NULL) */
+static void* getAndSetMcontextEip(ucontext_t *uc, void *eip) {
+#define GET_SET_RETURN(target_var, new_val) do {\
+    void *old_val = (void*)target_var; \
+    if (new_val) { \
+        void **temp = (void**)&target_var; \
+        *temp = new_val; \
+    } \
+    return old_val; \
+} while(0)
 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
     /* OSX < 10.6 */
     #if defined(__x86_64__)
-    return (void*) uc->uc_mcontext->__ss.__rip;
+    GET_SET_RETURN(uc->uc_mcontext->__ss.__rip, eip);
     #elif defined(__i386__)
-    return (void*) uc->uc_mcontext->__ss.__eip;
+    GET_SET_RETURN(uc->uc_mcontext->__ss.__eip, eip);
     #else
-    return (void*) uc->uc_mcontext->__ss.__srr0;
+    GET_SET_RETURN(uc->uc_mcontext->__ss.__srr0, eip);
     #endif
 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
     /* OSX >= 10.6 */
     #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
-    return (void*) uc->uc_mcontext->__ss.__rip;
+    GET_SET_RETURN(uc->uc_mcontext->__ss.__rip, eip);
     #elif defined(__i386__)
-    return (void*) uc->uc_mcontext->__ss.__eip;
+    GET_SET_RETURN(uc->uc_mcontext->__ss.__eip, eip);
     #else
     /* OSX ARM64 */
-    return (void*) arm_thread_state64_get_pc(uc->uc_mcontext->__ss);
+    void *old_val = (void*)arm_thread_state64_get_pc(uc->uc_mcontext->__ss);
+    if (eip) {
+        arm_thread_state64_set_pc_fptr(uc->uc_mcontext->__ss, eip);
+    }
+    return old_val;
     #endif
 #elif defined(__linux__)
     /* Linux */
     #if defined(__i386__) || ((defined(__X86_64__) || defined(__x86_64__)) && defined(__ILP32__))
-    return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
+    GET_SET_RETURN(uc->uc_mcontext.gregs[14], eip);
     #elif defined(__X86_64__) || defined(__x86_64__)
-    return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
+    GET_SET_RETURN(uc->uc_mcontext.gregs[16], eip);
     #elif defined(__ia64__) /* Linux IA64 */
-    return (void*) uc->uc_mcontext.sc_ip;
+    GET_SET_RETURN(uc->uc_mcontext.sc_ip, eip);
     #elif defined(__arm__) /* Linux ARM */
-    return (void*) uc->uc_mcontext.arm_pc;
+    GET_SET_RETURN(uc->uc_mcontext.arm_pc, eip);
     #elif defined(__aarch64__) /* Linux AArch64 */
-    return (void*) uc->uc_mcontext.pc;
+    GET_SET_RETURN(uc->uc_mcontext.pc, eip);
     #endif
 #elif defined(__FreeBSD__)
     /* FreeBSD */
     #if defined(__i386__)
-    return (void*) uc->uc_mcontext.mc_eip;
+    GET_SET_RETURN(uc->uc_mcontext.mc_eip, eip);
     #elif defined(__x86_64__)
-    return (void*) uc->uc_mcontext.mc_rip;
+    GET_SET_RETURN(uc->uc_mcontext.mc_rip, eip);
     #endif
 #elif defined(__OpenBSD__)
     /* OpenBSD */
     #if defined(__i386__)
-    return (void*) uc->sc_eip;
+    GET_SET_RETURN(uc->sc_eip, eip);
     #elif defined(__x86_64__)
-    return (void*) uc->sc_rip;
+    GET_SET_RETURN(uc->sc_rip, eip);
     #endif
 #elif defined(__NetBSD__)
     #if defined(__i386__)
-    return (void*) uc->uc_mcontext.__gregs[_REG_EIP];
+    GET_SET_RETURN(uc->uc_mcontext.__gregs[_REG_EIP], eip);
     #elif defined(__x86_64__)
-    return (void*) uc->uc_mcontext.__gregs[_REG_RIP];
+    GET_SET_RETURN(uc->uc_mcontext.__gregs[_REG_RIP], eip);
     #endif
 #elif defined(__DragonFly__)
-    return (void*) uc->uc_mcontext.mc_rip;
+    GET_SET_RETURN(uc->uc_mcontext.mc_rip, eip);
 #else
     return NULL;
 #endif
@@ -1800,6 +1814,10 @@ void dumpCodeAroundEIP(void *eip) {
     }
 }
 
+void invalidFunctionWasCalled() {}
+
+typedef void (*invalidFunctionWasCalledType)();
+
 void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
     UNUSED(secret);
     UNUSED(info);
@@ -1817,13 +1835,30 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
 
 #ifdef HAVE_BACKTRACE
     ucontext_t *uc = (ucontext_t*) secret;
-    void *eip = getMcontextEip(uc);
+    void *eip = getAndSetMcontextEip(uc, NULL);
     if (eip != NULL) {
         serverLog(LL_WARNING,
         "Crashed running the instruction at: %p", eip);
     }
 
-    logStackTrace(getMcontextEip(uc), 1);
+    if (eip == info->si_addr) {
+        /* When eip matches the bad address, it's an indication that we crashed when calling a non-mapped
+         * function pointer. In that case the call to backtrace will crash trying to access that address and we
+         * won't get a crash report logged. Set it to a valid point to avoid that crash. */
+
+        /* This trick allow to avoid compiler warning */
+        void *ptr;
+        invalidFunctionWasCalledType *ptr_ptr = (invalidFunctionWasCalledType*)&ptr;
+        *ptr_ptr = invalidFunctionWasCalled;
+        getAndSetMcontextEip(uc, ptr);
+    }
+
+    logStackTrace(eip, 1);
+
+    if (eip == info->si_addr) {
+        /* Restore old eip */
+        getAndSetMcontextEip(uc, eip);
+    }
 
     logRegisters(uc);
 #endif
@@ -1859,7 +1894,9 @@ void bugReportEnd(int killViaSignal, int sig) {
 "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
 "       Please report the crash by opening an issue on github:\n\n"
 "           http://github.com/redis/redis/issues\n\n"
+"  If a Redis module was involved, please open in the module's repo instead.\n\n"
 "  Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
+"  Some other issues could be detected by redis-server --check-system\n"
 );
 
     /* free(messages); Don't call free() with possibly corrupted memory. */
@@ -1918,7 +1955,7 @@ void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) {
 
     serverLogFromHandler(LL_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---");
 #ifdef HAVE_BACKTRACE
-    logStackTrace(getMcontextEip(uc), 1);
+    logStackTrace(getAndSetMcontextEip(uc, NULL), 1);
 #else
     serverLogFromHandler(LL_WARNING,"Sorry: no support for backtrace().");
 #endif
diff --git a/src/dict.c b/src/dict.c
index 1bbdc211e22..339e8c6c3ef 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -534,8 +534,8 @@ void *dictFetchValue(dict *d, const void *key) {
  * the fingerprint again when the iterator is released.
  * If the two fingerprints are different it means that the user of the iterator
  * performed forbidden operations against the dictionary while iterating. */
-long long dictFingerprint(dict *d) {
-    long long integers[6], hash = 0;
+unsigned long long dictFingerprint(dict *d) {
+    unsigned long long integers[6], hash = 0;
     int j;
 
     integers[0] = (long) d->ht[0].table;
diff --git a/src/dict.h b/src/dict.h
index 7e22589609b..3b7d46d71d8 100644
--- a/src/dict.h
+++ b/src/dict.h
@@ -95,7 +95,7 @@ typedef struct dictIterator {
     int table, safe;
     dictEntry *entry, *nextEntry;
     /* unsafe iterator fingerprint for misuse detection. */
-    long long fingerprint;
+    unsigned long long fingerprint;
 } dictIterator;
 
 typedef void (dictScanFunction)(void *privdata, const dictEntry *de);
diff --git a/src/evict.c b/src/evict.c
index d3cdaaa80b4..ab46d1af601 100644
--- a/src/evict.c
+++ b/src/evict.c
@@ -549,7 +549,7 @@ int performEvictions(void) {
         {
             struct evictionPoolEntry *pool = EvictionPoolLRU;
 
-            while(bestkey == NULL) {
+            while (bestkey == NULL) {
                 unsigned long total_keys = 0, keys;
 
                 /* We don't want to make local-db choices when expiring keys,
@@ -695,12 +695,18 @@ int performEvictions(void) {
         /* At this point, we have run out of evictable items.  It's possible
          * that some items are being freed in the lazyfree thread.  Perform a
          * short wait here if such jobs exist, but don't wait long.  */
-        if (bioPendingJobsOfType(BIO_LAZY_FREE)) {
-            usleep(eviction_time_limit_us);
+        mstime_t lazyfree_latency;
+        latencyStartMonitor(lazyfree_latency);
+        while (bioPendingJobsOfType(BIO_LAZY_FREE) &&
+              elapsedUs(evictionTimer) < eviction_time_limit_us) {
             if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {
                 result = EVICT_OK;
+                break;
             }
+            usleep(eviction_time_limit_us < 1000 ? eviction_time_limit_us : 1000);
         }
+        latencyEndMonitor(lazyfree_latency);
+        latencyAddSampleIfNeeded("eviction-lazyfree",lazyfree_latency);
     }
 
     latencyEndMonitor(latency);
diff --git a/src/module.c b/src/module.c
index 26ab125aa6a..f6c51b2a555 100644
--- a/src/module.c
+++ b/src/module.c
@@ -8321,7 +8321,8 @@ void moduleFireServerEvent(uint64_t eid, int subid, void *data) {
              * up to the specific event setup to change it when it makes
              * sense. For instance for FLUSHDB events we select the correct
              * DB automatically. */
-            selectDb(ctx.client, 0);
+            if (!real_client_used)
+                selectDb(ctx.client, 0);
 
             /* Event specific context and data pointer setup. */
             if (eid == REDISMODULE_EVENT_CLIENT_CHANGE) {
@@ -9330,6 +9331,8 @@ void moduleRegisterCoreAPI(void) {
     REGISTER_API(StringTruncate);
     REGISTER_API(SetExpire);
     REGISTER_API(GetExpire);
+    REGISTER_API(SetAbsExpire);
+    REGISTER_API(GetAbsExpire);
     REGISTER_API(ResetDataset);
     REGISTER_API(DbSize);
     REGISTER_API(RandomKey);
diff --git a/src/pqsort.c b/src/pqsort.c
index 508c09f920f..fab54e026a2 100644
--- a/src/pqsort.c
+++ b/src/pqsort.c
@@ -38,7 +38,7 @@
  */
 
 #include <sys/types.h>
-
+#include <stdint.h>
 #include <errno.h>
 #include <stdlib.h>
 
@@ -62,7 +62,7 @@ static inline void	 swapfunc (char *, char *, size_t, int);
         } while (--i > 0);				\
 }
 
-#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+#define SWAPINIT(a, es) swaptype = (uintptr_t)a % sizeof(long) || \
 	es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
 
 static inline void
diff --git a/src/quicklist.c b/src/quicklist.c
index a9f8b43b14b..f988802f0e5 100644
--- a/src/quicklist.c
+++ b/src/quicklist.c
@@ -1864,7 +1864,7 @@ int quicklistTest(int argc, char *argv[], int accurate) {
             unsigned int sz;
             long long lv;
             ql_info(ql);
-            quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);
+            assert(quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv));
             assert(data != NULL);
             assert(sz == 32);
             if (strcmp(populate, (char *)data))
@@ -1882,7 +1882,7 @@ int quicklistTest(int argc, char *argv[], int accurate) {
             unsigned int sz;
             long long lv;
             ql_info(ql);
-            quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);
+            assert(quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv));
             assert(data == NULL);
             assert(lv == 55513);
             ql_verify(ql, 0, 0, 0, 0);
@@ -2681,9 +2681,11 @@ int quicklistTest(int argc, char *argv[], int accurate) {
                         if (step == 1) {
                             for (int i = 0; i < list_sizes[list] / 2; i++) {
                                 unsigned char *data;
-                                quicklistPop(ql, QUICKLIST_HEAD, &data, NULL, NULL);
+                                assert(quicklistPop(ql, QUICKLIST_HEAD, &data,
+                                                    NULL, NULL));
                                 zfree(data);
-                                quicklistPop(ql, QUICKLIST_TAIL, &data, NULL, NULL);
+                                assert(quicklistPop(ql, QUICKLIST_TAIL, &data,
+                                                    NULL, NULL));
                                 zfree(data);
                             }
                         }
diff --git a/src/rax.c b/src/rax.c
index 0826b974afc..2e79aed7c66 100644
--- a/src/rax.c
+++ b/src/rax.c
@@ -1270,6 +1270,7 @@ void raxStart(raxIterator *it, rax *rt) {
  * is a low level function used to implement the iterator, not callable by
  * the user. Returns 0 on out of memory, otherwise 1 is returned. */
 int raxIteratorAddChars(raxIterator *it, unsigned char *s, size_t len) {
+    if (len == 0) return 1;
     if (it->key_max < it->key_len+len) {
         unsigned char *old = (it->key == it->key_static_string) ? NULL :
                                                                   it->key;
diff --git a/src/rdb.c b/src/rdb.c
index 92489151d88..5cd85ac07c5 100644
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -288,12 +288,16 @@ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags, size_t *lenptr) {
     } else if (enctype == RDB_ENC_INT16) {
         uint16_t v;
         if (rioRead(rdb,enc,2) == 0) return NULL;
-        v = enc[0]|(enc[1]<<8);
+        v = ((uint32_t)enc[0])|
+            ((uint32_t)enc[1]<<8);
         val = (int16_t)v;
     } else if (enctype == RDB_ENC_INT32) {
         uint32_t v;
         if (rioRead(rdb,enc,4) == 0) return NULL;
-        v = enc[0]|(enc[1]<<8)|(enc[2]<<16)|(enc[3]<<24);
+        v = ((uint32_t)enc[0])|
+            ((uint32_t)enc[1]<<8)|
+            ((uint32_t)enc[2]<<16)|
+            ((uint32_t)enc[3]<<24);
         val = (int32_t)v;
     } else {
         rdbReportCorruptRDB("Unknown RDB integer encoding type %d",enctype);
@@ -2915,6 +2919,10 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
 
         rioInitWithFd(&rdb,rdb_pipe_write);
 
+        /* Close the reading part, so that if the parent crashes, the child will
+         * get a write error and exit. */
+        close(server.rdb_pipe_read);
+
         redisSetProcTitle("redis-rdb-to-slaves");
         redisSetCpuAffinity(server.bgsave_cpulist);
 
diff --git a/src/scripting.c b/src/scripting.c
index 2d021ddafc4..9b926e8d05a 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -468,7 +468,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
         /* Handle error reply. */
         /* we took care of the stack size on function start */
         lua_pushstring(lua,"err");
-        lua_gettable(lua,-2);
+        lua_rawget(lua,-2);
         t = lua_type(lua,-1);
         if (t == LUA_TSTRING) {
             addReplyErrorFormat(c,"-%s",lua_tostring(lua,-1));
@@ -479,7 +479,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
 
         /* Handle status reply. */
         lua_pushstring(lua,"ok");
-        lua_gettable(lua,-2);
+        lua_rawget(lua,-2);
         t = lua_type(lua,-1);
         if (t == LUA_TSTRING) {
             sds ok = sdsnew(lua_tostring(lua,-1));
@@ -493,7 +493,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
 
         /* Handle double reply. */
         lua_pushstring(lua,"double");
-        lua_gettable(lua,-2);
+        lua_rawget(lua,-2);
         t = lua_type(lua,-1);
         if (t == LUA_TNUMBER) {
             addReplyDouble(c,lua_tonumber(lua,-1));
@@ -504,7 +504,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
 
         /* Handle map reply. */
         lua_pushstring(lua,"map");
-        lua_gettable(lua,-2);
+        lua_rawget(lua,-2);
         t = lua_type(lua,-1);
         if (t == LUA_TTABLE) {
             int maplen = 0;
@@ -527,7 +527,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
 
         /* Handle set reply. */
         lua_pushstring(lua,"set");
-        lua_gettable(lua,-2);
+        lua_rawget(lua,-2);
         t = lua_type(lua,-1);
         if (t == LUA_TTABLE) {
             int setlen = 0;
@@ -554,7 +554,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
         while(1) {
             /* we took care of the stack size on function start */
             lua_pushnumber(lua,j++);
-            lua_gettable(lua,-2);
+            lua_rawget(lua,-2);
             t = lua_type(lua,-1);
             if (t == LUA_TNIL) {
                 lua_pop(lua,1);
diff --git a/src/sentinel.c b/src/sentinel.c
index 50ac18d9feb..09fec69fc39 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -658,10 +658,16 @@ int sentinelAddrOrHostnameEqual(sentinelAddr *a, sentinelAddr *b) {
 int sentinelAddrEqualsHostname(sentinelAddr *a, char *hostname) {
     char ip[NET_IP_STR_LEN];
 
-    /* We always resolve the hostname and compare it to the address */
+    /* Try resolve the hostname and compare it to the address */
     if (anetResolve(NULL, hostname, ip, sizeof(ip),
-                    sentinel.resolve_hostnames ? ANET_NONE : ANET_IP_ONLY) == ANET_ERR)
-        return 0;
+                    sentinel.resolve_hostnames ? ANET_NONE : ANET_IP_ONLY) == ANET_ERR) {
+
+        /* If failed resolve then compare based on hostnames. That is our best effort as
+         * long as the server is unavailable for some reason. It is fine since Redis 
+         * instance cannot have multiple hostnames for a given setup */
+        return !strcasecmp(sentinel.resolve_hostnames ? a->hostname : a->ip, hostname);
+    }
+    /* Compare based on address */
     return !strcasecmp(a->ip, ip);
 }
 
@@ -4024,6 +4030,7 @@ void sentinelSetCommand(client *c) {
     int j, changes = 0;
     int badarg = 0; /* Bad argument position for error reporting. */
     char *option;
+    int redacted;
 
     if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
         == NULL) return;
@@ -4034,6 +4041,7 @@ void sentinelSetCommand(client *c) {
         option = c->argv[j]->ptr;
         long long ll;
         int old_j = j; /* Used to know what to log as an event. */
+        redacted = 0;
 
         if (!strcasecmp(option,"down-after-milliseconds") && moreargs > 0) {
             /* down-after-millisecodns <milliseconds> */
@@ -4108,6 +4116,7 @@ void sentinelSetCommand(client *c) {
             sdsfree(ri->auth_pass);
             ri->auth_pass = strlen(value) ? sdsnew(value) : NULL;
             changes++;
+            redacted = 1;
         } else if (!strcasecmp(option,"auth-user") && moreargs > 0) {
             /* auth-user <username> */
             char *value = c->argv[++j]->ptr;
@@ -4155,7 +4164,7 @@ void sentinelSetCommand(client *c) {
         switch(numargs) {
         case 2:
             sentinelEvent(LL_WARNING,"+set",ri,"%@ %s %s",(char*)c->argv[old_j]->ptr,
-                                                          (char*)c->argv[old_j+1]->ptr);
+                                                          redacted ? "******" : (char*)c->argv[old_j+1]->ptr);
             break;
         case 3:
             sentinelEvent(LL_WARNING,"+set",ri,"%@ %s %s %s",(char*)c->argv[old_j]->ptr,
diff --git a/src/server.c b/src/server.c
index d887e58d68d..49e8051504a 100644
--- a/src/server.c
+++ b/src/server.c
@@ -5457,8 +5457,15 @@ int linuxOvercommitMemoryValue(void) {
 }
 
 void linuxMemoryWarnings(void) {
-    if (linuxOvercommitMemoryValue() == 0) {
-        serverLog(LL_WARNING,"WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.");
+    if (linuxOvercommitMemoryValue() != 1) {
+        const char *msg =
+            "WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. "
+#if defined(USE_JEMALLOC)
+            "Being disabled, it can can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. "
+#endif
+            "To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the "
+            "command 'sysctl vm.overcommit_memory=1' for this to take effect.";
+        serverLog(LL_WARNING, "%s", msg);
     }
     if (THPIsEnabled() && THPDisable()) {
         serverLog(LL_WARNING,"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').");
@@ -5917,6 +5924,10 @@ int redisFork(int purpose) {
         setOOMScoreAdj(CONFIG_OOM_BGCHILD);
         setupChildSignalHandlers();
         closeChildUnusedResourceAfterFork();
+        /* Close the reading part, so that if the parent crashes, the child will
+         * get a write error and exit. */
+        if (server.child_info_pipe[0] != -1)
+            close(server.child_info_pipe[0]);
     } else {
         /* Parent */
         server.stat_total_forks++;
diff --git a/src/sha256.c b/src/sha256.c
index d644d2d4e5b..3b53c45c414 100644
--- a/src/sha256.c
+++ b/src/sha256.c
@@ -45,8 +45,13 @@ void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
 {
 	WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
 
-	for (i = 0, j = 0; i < 16; ++i, j += 4)
-		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+    for (i = 0, j = 0; i < 16; ++i, j += 4) {
+        m[i] = ((WORD) data[j + 0] << 24) |
+               ((WORD) data[j + 1] << 16) |
+               ((WORD) data[j + 2] << 8) |
+               ((WORD) data[j + 3]);
+    }
+
 	for ( ; i < 64; ++i)
 		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
 
diff --git a/src/t_zset.c b/src/t_zset.c
index f313b8850dc..b74ea7eb49f 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -1192,9 +1192,10 @@ void zsetConvert(robj *zobj, int encoding) {
         zs->zsl = zslCreate();
 
         eptr = ziplistIndex(zl,0);
-        serverAssertWithInfo(NULL,zobj,eptr != NULL);
-        sptr = ziplistNext(zl,eptr);
-        serverAssertWithInfo(NULL,zobj,sptr != NULL);
+        if (eptr != NULL) {
+            sptr = ziplistNext(zl,eptr);
+            serverAssertWithInfo(NULL,zobj,sptr != NULL);
+        }
 
         while (eptr != NULL) {
             score = zzlGetScore(sptr);
@@ -2964,8 +2965,10 @@ static void zrangeResultFinalizeClient(zrange_result_handler *handler,
 /* Result handler methods for storing the ZRANGESTORE to a zset. */
 static void zrangeResultBeginStore(zrange_result_handler *handler, long length)
 {
-    UNUSED(length);
-    handler->dstobj = createZsetZiplistObject();
+    if (length > (long)server.zset_max_ziplist_entries)
+        handler->dstobj = createZsetObject();
+    else
+        handler->dstobj = createZsetZiplistObject();
 }
 
 static void zrangeResultEmitCBufferForStore(zrange_result_handler *handler,
diff --git a/src/tls.c b/src/tls.c
index 3657ab43966..29f6d0e065e 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -715,6 +715,8 @@ static void connTLSClose(connection *conn_) {
     tls_connection *conn = (tls_connection *) conn_;
 
     if (conn->ssl) {
+        if (conn->c.state == CONN_STATE_CONNECTED)
+            SSL_shutdown(conn->ssl);
         SSL_free(conn->ssl);
         conn->ssl = NULL;
     }
diff --git a/src/tracking.c b/src/tracking.c
index 1ebc3cae833..1584bdbceca 100644
--- a/src/tracking.c
+++ b/src/tracking.c
@@ -381,7 +381,7 @@ void trackingInvalidateKey(client *c, robj *keyobj, int bcast) {
         /* If the client enabled the NOLOOP mode, don't send notifications
          * about keys changed by the client itself. */
         if (target->flags & CLIENT_TRACKING_NOLOOP &&
-            target == c)
+            target == server.current_client)
         {
             continue;
         }
@@ -416,9 +416,15 @@ void trackingHandlePendingKeyInvalidations() {
         robj *key = listNodeValue(ln);
         /* current_client maybe freed, so we need to send invalidation
          * message only when current_client is still alive */
-        if (server.current_client != NULL)
-            sendTrackingMessage(server.current_client,(char *)key->ptr,sdslen(key->ptr),0);
-        decrRefCount(key);
+        if (server.current_client != NULL) {
+            if (key != NULL) {
+                sendTrackingMessage(server.current_client,(char *)key->ptr,sdslen(key->ptr),0);
+            } else {
+                sendTrackingMessage(server.current_client,shared.null[server.current_client->resp]->ptr,
+                    sdslen(shared.null[server.current_client->resp]->ptr),1);
+            }
+        }
+        if (key != NULL) decrRefCount(key);
     }
     listEmpty(server.tracking_pending_keys);
 }
@@ -447,7 +453,12 @@ void trackingInvalidateKeysOnFlush(int async) {
         while ((ln = listNext(&li)) != NULL) {
             client *c = listNodeValue(ln);
             if (c->flags & CLIENT_TRACKING) {
-                sendTrackingMessage(c,shared.null[c->resp]->ptr,sdslen(shared.null[c->resp]->ptr),1);
+                if (c == server.current_client) {
+                    /* We use a special NULL to indicate that we should send null */
+                    listAddNodeTail(server.tracking_pending_keys,NULL);
+                } else {
+                    sendTrackingMessage(c,shared.null[c->resp]->ptr,sdslen(shared.null[c->resp]->ptr),1);
+                }
             }
         }
     }
diff --git a/src/version.h b/src/version.h
index 164ae932365..c910a2d4288 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,2 +1,2 @@
-#define REDIS_VERSION "6.2.7"
-#define REDIS_VERSION_NUM 0x00060207
+#define REDIS_VERSION "6.2.8"
+#define REDIS_VERSION_NUM 0x00060208
diff --git a/src/ziplist.c b/src/ziplist.c
index 7a3263d8c66..b58e6cb9799 100644
--- a/src/ziplist.c
+++ b/src/ziplist.c
@@ -416,10 +416,10 @@ unsigned int zipStoreEntryEncoding(unsigned char *p, unsigned char encoding, uns
             (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1];                       \
         } else if ((encoding) == ZIP_STR_32B) {                                \
             (lensize) = 5;                                                     \
-            (len) = ((ptr)[1] << 24) |                                         \
-                    ((ptr)[2] << 16) |                                         \
-                    ((ptr)[3] <<  8) |                                         \
-                    ((ptr)[4]);                                                \
+            (len) = ((uint32_t)(ptr)[1] << 24) |                               \
+                    ((uint32_t)(ptr)[2] << 16) |                               \
+                    ((uint32_t)(ptr)[3] <<  8) |                               \
+                    ((uint32_t)(ptr)[4]);                                      \
         } else {                                                               \
             (lensize) = 0; /* bad encoding, should be covered by a previous */ \
             (len) = 0;     /* ZIP_ASSERT_ENCODING / zipEncodingLenSize, or  */ \
@@ -557,7 +557,7 @@ void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
         memcpy(p,&i16,sizeof(i16));
         memrev16ifbe(p);
     } else if (encoding == ZIP_INT_24B) {
-        i32 = value<<8;
+        i32 = ((uint64_t)value)<<8;
         memrev32ifbe(&i32);
         memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t));
     } else if (encoding == ZIP_INT_32B) {


Test patch:
diff --git a/tests/integration/psync2.tcl b/tests/integration/psync2.tcl
index 8f8db5a92d1..43ff567bf81 100644
--- a/tests/integration/psync2.tcl
+++ b/tests/integration/psync2.tcl
@@ -232,6 +232,10 @@ start_server {} {
         # In absence of pings, are the instances really able to have
         # the exact same offset?
         $R($master_id) config set repl-ping-replica-period 3600
+        for {set j 0} {$j < 5} {incr j} {
+            if {$j == $master_id} continue
+            $R($j) config set repl-timeout 10000
+        }
         wait_for_condition 500 100 {
             [status $R($master_id) master_repl_offset] == [status $R(0) master_repl_offset] &&
             [status $R($master_id) master_repl_offset] == [status $R(1) master_repl_offset] &&
diff --git a/tests/integration/replication.tcl b/tests/integration/replication.tcl
index 916dd324ef8..ed8e29acc61 100644
--- a/tests/integration/replication.tcl
+++ b/tests/integration/replication.tcl
@@ -754,7 +754,7 @@ test "diskless replication child being killed is collected" {
             # wait for the replicas to start reading the rdb
             wait_for_log_messages 0 {"*Loading DB in memory*"} $loglines 800 10
 
-            # wait to be sure the eplica is hung and the master is blocked on write
+            # wait to be sure the replica is hung and the master is blocked on write
             after 500
 
             # simulate the OOM killer or anyone else kills the child
@@ -771,6 +771,45 @@ test "diskless replication child being killed is collected" {
     }
 }
 
+foreach mdl {yes no} {
+    test "replication child dies when parent is killed - diskless: $mdl" {
+        # when master is killed, make sure the fork child can detect that and exit
+        start_server {tags {"repl"}} {
+            set master [srv 0 client]
+            set master_host [srv 0 host]
+            set master_port [srv 0 port]
+            set master_pid [srv 0 pid]
+            $master config set repl-diskless-sync $mdl
+            $master config set repl-diskless-sync-delay 0
+            # create keys that will take 10 seconds to save
+            $master config set rdb-key-save-delay 1000
+            $master debug populate 10000
+            start_server {} {
+                set replica [srv 0 client]
+                $replica replicaof $master_host $master_port
+
+                # wait for rdb child to start
+                wait_for_condition 5000 10 {
+                    [s -1 rdb_bgsave_in_progress] == 1
+                } else {
+                    fail "rdb child didn't start"
+                }
+                set fork_child_pid [get_child_pid -1]
+
+                # simulate the OOM killer or anyone else kills the parent
+                exec kill -9 $master_pid
+
+                # wait for the child to notice the parent died have exited
+                wait_for_condition 500 10 {
+                    [process_is_alive $fork_child_pid] == 0
+                } else {
+                    fail "rdb child didn't terminate"
+                }
+            }
+        }
+    } {} {external:skip}
+}
+
 test "diskless replication read pipe cleanup" {
     # In diskless replication, we create a read pipe for the RDB, between the child and the parent.
     # When we close this pipe (fd), the read handler also needs to be removed from the event loop (if it still registered).
diff --git a/tests/modules/Makefile b/tests/modules/Makefile
index 021110292bd..727e64cd10a 100644
--- a/tests/modules/Makefile
+++ b/tests/modules/Makefile
@@ -10,10 +10,9 @@ else	# Linux, others
 	SHOBJ_LDFLAGS ?= -shared
 endif
 
-# Needed to satisfy __stack_chk_fail_local on Linux with -m32, due to gcc
-# -fstack-protector by default. Breaks on FreeBSD so we exclude it.
-ifneq ($(uname_S),FreeBSD)
-    LIBS = -lc
+ifeq ($(uname_S),Linux)
+    LD = gcc
+    CC = gcc
 endif
 
 # OS X 11.x doesn't have /usr/lib/libSystem.dylib and needs an explicit setting.
@@ -53,7 +52,7 @@ TEST_MODULES = \
 all: $(TEST_MODULES)
 
 32bit:
-	$(MAKE) CFLAGS="-m32" LDFLAGS="-melf_i386"
+	$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
 
 %.xo: %.c ../../src/redismodule.h
 	$(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@
diff --git a/tests/support/redis.tcl b/tests/support/redis.tcl
index fa9c0650135..f7d28904499 100644
--- a/tests/support/redis.tcl
+++ b/tests/support/redis.tcl
@@ -66,6 +66,33 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {re
     interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id
 }
 
+# On recent versions of tcl-tls/OpenSSL, reading from a dropped connection
+# results with an error we need to catch and mimic the old behavior.
+proc ::redis::redis_safe_read {fd len} {
+    if {$len == -1} {
+        set err [catch {set val [read $fd]} msg]
+    } else {
+        set err [catch {set val [read $fd $len]} msg]
+    }
+    if {!$err} {
+        return $val
+    }
+    if {[string match "*connection abort*" $msg]} {
+        return {}
+    }
+    error $msg
+}
+
+proc ::redis::redis_safe_gets {fd} {
+    if {[catch {set val [gets $fd]} msg]} {
+        if {[string match "*connection abort*" $msg]} {
+            return {}
+        }
+        error $msg
+    }
+    return $val
+}
+
 # This is a wrapper to the actual dispatching procedure that handles
 # reconnection if needed.
 proc ::redis::__dispatch__ {id method args} {
@@ -192,8 +219,8 @@ proc ::redis::redis_writenl {fd buf} {
 }
 
 proc ::redis::redis_readnl {fd len} {
-    set buf [read $fd $len]
-    read $fd 2 ; # discard CR LF
+    set buf [redis_safe_read $fd $len]
+    redis_safe_read $fd 2 ; # discard CR LF
     return $buf
 }
 
@@ -239,11 +266,11 @@ proc ::redis::redis_read_map {id fd} {
 }
 
 proc ::redis::redis_read_line fd {
-    string trim [gets $fd]
+    string trim [redis_safe_gets $fd]
 }
 
 proc ::redis::redis_read_null fd {
-    gets $fd
+    redis_safe_gets $fd
     return {}
 }
 
@@ -266,7 +293,7 @@ proc ::redis::redis_read_reply {id fd} {
     }
 
     while {1} {
-        set type [read $fd 1]
+        set type [redis_safe_read $fd 1]
         switch -exact -- $type {
             _ {return [redis_read_null $fd]}
             : -
diff --git a/tests/support/util.tcl b/tests/support/util.tcl
index f6dbc51979b..dc1ef6dc25e 100644
--- a/tests/support/util.tcl
+++ b/tests/support/util.tcl
@@ -565,6 +565,15 @@ proc get_child_pid {idx} {
     return $child_pid
 }
 
+proc process_is_alive pid {
+    if {[catch {exec ps -p $pid} err]} {
+        return 0
+    } else {
+        if {[string match "*<defunct>*" $err]} { return 0 }
+        return 1
+    }
+}
+
 proc cmdrstat {cmd r} {
     if {[regexp "\r\ncmdstat_$cmd:(.*?)\r\n" [$r info commandstats] _ value]} {
         set _ $value
diff --git a/tests/unit/moduleapi/hooks.tcl b/tests/unit/moduleapi/hooks.tcl
index c4af59bd236..4e1103a619a 100644
--- a/tests/unit/moduleapi/hooks.tcl
+++ b/tests/unit/moduleapi/hooks.tcl
@@ -12,6 +12,19 @@ tags "modules" {
             assert {[r hooks.event_count client-disconnected] > 1}
         }
 
+        test {Test module client change event for blocked client} {
+            set rd [redis_deferring_client]
+            # select db other than 0
+            $rd select 1
+            # block on key
+            $rd brpop foo 0
+            # kill blocked client
+            r client kill skipme yes
+            # assert server is still up
+            assert_equal [r ping] PONG
+            $rd close
+        } 
+
         test {Test module cron hook} {
             after 100
             assert {[r hooks.event_count cron-loop] > 0}
diff --git a/tests/unit/obuf-limits.tcl b/tests/unit/obuf-limits.tcl
index 3f26c53f94a..fd7e38a0d20 100644
--- a/tests/unit/obuf-limits.tcl
+++ b/tests/unit/obuf-limits.tcl
@@ -111,7 +111,7 @@ start_server {tags {"obuf-limits"}} {
 
         # Read nothing
         set fd [$rd channel]
-        assert_equal {} [read $fd]
+        assert_equal {} [$rd rawread]
     }
 
     # Note: This test assumes that what's written with one write, will be read by redis in one read.
@@ -151,8 +151,7 @@ start_server {tags {"obuf-limits"}} {
         assert_equal "PONG" [r ping]
         set clients [r client list]
         assert_no_match "*name=multicommands*" $clients
-        set fd [$rd2 channel]
-        assert_equal {} [read $fd]
+        assert_equal {} [$rd2 rawread]
     }
 
     test {Execute transactions completely even if client output buffer limit is enforced} {
diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl
index 99f249afc27..9f5ee773479 100644
--- a/tests/unit/scripting.tcl
+++ b/tests/unit/scripting.tcl
@@ -3,6 +3,20 @@ start_server {tags {"scripting"}} {
         r eval {return 'hello'} 0
     } {hello}
 
+    test {EVAL - Return _G} {
+        r eval {return _G} 0
+    } {}
+
+    test {EVAL - Return table with a metatable that raise error} {
+        r eval {local a = {}; setmetatable(a,{__index=function() foo() end}) return a} 0
+    } {}
+
+    test {EVAL - Return table with a metatable that call redis} {
+        r eval {local a = {}; setmetatable(a,{__index=function() redis.call('set', 'x', '1') end}) return a} 0
+        # make sure x was not set
+        r get x
+    } {}
+
     test {EVAL - Lua integer -> Redis protocol type conversion} {
         r eval {return 100.5} 0
     } {100}
diff --git a/tests/unit/tracking.tcl b/tests/unit/tracking.tcl
index 57c17180e76..c1d43614f4a 100644
--- a/tests/unit/tracking.tcl
+++ b/tests/unit/tracking.tcl
@@ -208,6 +208,19 @@ start_server {tags {"tracking network"}} {
         assert {$res eq {key1}}
     }
 
+    test {Invalid keys should not be tracked for scripts in NOLOOP mode} {
+        $rd_sg CLIENT TRACKING off
+        $rd_sg CLIENT TRACKING on NOLOOP
+        $rd_sg HELLO 3
+        $rd_sg SET key1 1
+        assert_equal "1" [$rd_sg GET key1]
+
+        # For write command in script, invalid key should not be tracked with NOLOOP flag
+        $rd_sg eval "return redis.call('set', 'key1', '2')" 1 key1
+        assert_equal "2" [$rd_sg GET key1]
+        $rd_sg CLIENT TRACKING off
+    }
+
     test {RESP3 Client gets tracking-redir-broken push message after cached key changed when rediretion client is terminated} {
         r CLIENT TRACKING on REDIRECT $redir_id
         $rd_sg SET key1 1
@@ -517,6 +530,20 @@ start_server {tags {"tracking network"}} {
         assert_equal [lindex [$rd_redirection read] 2] {}
     }
 
+    test {flushdb tracking invalidation message is not interleaved with transaction response} {
+        clean_all
+        r HELLO 3
+        r CLIENT TRACKING on
+        r SET a{t} 1
+        r GET a{t}
+        r MULTI
+        r FLUSHDB
+        set res [r EXEC]
+        assert_equal $res {OK}
+        # Consume the invalidate message which is after command response
+        r read
+    } {invalidate {}}
+
     # Keys are defined to be evicted 100 at a time by default.
     # If after eviction the number of keys still surpasses the limit
     # defined in tracking-table-max-keys, we increases eviction 
diff --git a/tests/unit/type/zset.tcl b/tests/unit/type/zset.tcl
index 94b2ab48015..18bae4440fb 100644
--- a/tests/unit/type/zset.tcl
+++ b/tests/unit/type/zset.tcl
@@ -1643,6 +1643,27 @@ start_server {tags {"zset"}} {
         assert_match "*syntax*" $err
     }
 
+    test {ZRANGESTORE with zset-max-ziplist-entries 0 #10767 case} {
+        set original_max [lindex [r config get zset-max-ziplist-entries] 1]
+        r config set zset-max-ziplist-entries 0
+        r del z1{t} z2{t}
+        r zadd z1{t} 1 a
+        assert_equal 1 [r zrangestore z2{t} z1{t} 0 -1]
+        r config set zset-max-ziplist-entries $original_max
+    }
+
+    test {ZRANGESTORE with zset-max-ziplist-entries 1 dst key should use skiplist encoding} {
+        set original_max [lindex [r config get zset-max-ziplist-entries] 1]
+        r config set zset-max-ziplist-entries 1
+        r del z1{t} z2{t} z3{t}
+        r zadd z1{t} 1 a 2 b
+        assert_equal 1 [r zrangestore z2{t} z1{t} 0 0]
+        assert_encoding ziplist z2{t}
+        assert_equal 2 [r zrangestore z3{t} z1{t} 0 1]
+        assert_encoding skiplist z3{t}
+        r config set zset-max-ziplist-entries $original_max
+    }
+
     test {ZRANGE invalid syntax} {
         catch {r zrange z1 0 -1 limit 1 2} err
         assert_match "*syntax*" $err