1. 01 Dec, 2021 3 commits
    • meir@redislabs.com's avatar
      Redis Functions - Move Lua related variable into luaCtx struct · e0cd580a
      meir@redislabs.com authored
      The following variable was renamed:
      1. lua_caller 			-> script_caller
      2. lua_time_limit 		-> script_time_limit
      3. lua_timedout 		-> script_timedout
      4. lua_oom 			-> script_oom
      5. lua_disable_deny_script 	-> script_disable_deny_script
      6. in_eval			-> in_script
      
      The following variables was moved to lctx under eval.c
      1.  lua
      2.  lua_client
      3.  lua_cur_script
      4.  lua_scripts
      5.  lua_scripts_mem
      6.  lua_replicate_commands
      7.  lua_write_dirty
      8.  lua_random_dirty
      9.  lua_multi_emitted
      10. lua_repl
      11. lua_kill
      12. lua_time_start
      13. lua_time_snapshot
      
      This commit is in a low risk of introducing any issues and it
      is just moving varibales around and not changing any logic.
      e0cd580a
    • meir@redislabs.com's avatar
      Redis Functions - Move code to make review process easier. · 22aab1ce
      meir@redislabs.com authored
      This commit is only move code around without changing it.
      The reason behind this is to make review process easier
      by allowing the reviewer to simply ignore all code movements.
      
      changes:
      1. rename scripting.c to eval.c
      2. introduce and new file, script_lua.c, and move parts of Lua
         functionality to this new file. script_lua.c will eventually
         contains the shared code between legacy lua and lua engine.
      
      This commit does not compiled on purpose. Its only purpose is to move
      code and rename files.
      22aab1ce
    • yoav-steinberg's avatar
      Multiparam config set (#9748) · 0e5b813e
      yoav-steinberg authored
      We can now do: `config set maxmemory 10m repl-backlog-size 5m`
      
      ## Basic algorithm to support "transaction like" config sets:
      
      1. Backup all relevant current values (via get).
      2. Run "verify" and "set" on everything, if we fail run "restore".
      3. Run "apply" on everything (optional optimization: skip functions already run). If we fail run "restore".
      4. Return success.
      
      ### restore
      1. Run set on everything in backup. If we fail log it and continue (this puts us in an undefined
         state but we decided it's better than the alternative of panicking). This indicates either a bug
         or some unsupported external state.
      2. Run apply on everything in backup (optimization: skip functions already run). If we fail log
         it (see comment above).
      3. Return error.
      
      ## Implementation/design changes:
      * Apply function are idempotent (have no effect if they are run more than once for the same config).
      * No indication in set functions if we're reading the config or running from the `CONFIG SET` command
         (removed `update` argument).
      * Set function should set some config variable and assume an (optional) apply function will use that
         later to apply. If we know this setting can be safely applied immediately and can always be reverted
         and doesn't depend on any other configuration we can apply immediately from within the set function
         (and not store the setting anywhere). This is the case of this `dir` config, for example, which has no
         apply function. No apply function is need also in the case that setting the variable in the `server` struct
         is all that needs to be done to make the configuration take effect. Note that the original concept of `update_fn`,
         which received the old and new values was removed and replaced by the optional apply function.
      * Apply functions use settings written to the `server` struct and don't receive any inputs.
      * I take care that for the generic (non-special) configs if there's no change I avoid calling the setter (possible
         optimization: avoid calling the apply function as well).
      * Passing the same config parameter more than once to `config set` will fail. You can't do `config set my-setting
         value1 my-setting value2`.
      
      Note that getting `save` in the context of the conf file parsing to work here as before was a pain.
      The conf file supports an aggregate `save` definition, where each `save` line is added to the server's
      save params. This is unlike any other line in the config file where each line overwrites any previous
      configuration. Since we now support passing multiple save params in a single line (see top comments
      about `save` in https://github.com/redis/redis/pull/9644) we should deprecate the aggregate nature of
      this config line and perhaps reduce this ugly code in the future.
      0e5b813e
  2. 30 Nov, 2021 5 commits
    • Itamar Haber's avatar
      Adds auto-seq-only-generation via `XADD ... <ms>-*` (#9217) · 21aa1d4b
      Itamar Haber authored
      Adds the ability to autogenerate the sequence part of the millisecond-only explicit ID specified for `XADD`. This is useful in case added entries have an externally-provided timestamp without sub-millisecond resolution.
      21aa1d4b
    • Wen Hui's avatar
      Sentinel master reboot fix (#9438) · 2afa41f6
      Wen Hui authored
      Add master-reboot-down-after-period as a configurable parameter, to make it possible to trigger a failover from a master that is responding with `-LOADING` for a long time after being restarted.
      2afa41f6
    • 丽媛自己动's avatar
      modify misleading note in comment (#9865) · af072c26
      丽媛自己动 authored
      now rdbSaveInfo used in both way, so i think we should update previous notes, in case of misleading
      af072c26
    • Viktor Söderqvist's avatar
      bdf531e3
    • Meir Shpilraien (Spielrein)'s avatar
      Swap '\r\n' with spaces when returning a big number reply from Lua script. (#9870) · b8e82d20
      Meir Shpilraien (Spielrein) authored
      The issue can only happened with a bad Lua script that claims to return
      a big number while actually return data which is not a big number (contains
      chars that are not digits). Such thing will not cause an issue unless the big
      number value contains `\r\n` and then it messes the resp3 structure. The fix
      changes all the appearances of '\r\n' with spaces.
      
      Such an issue can also happened on simple string or error replies but those
      already handle it the same way this PR does (replace `\r\n` with spaces).
      
      Other replies type are not vulnerable to this issue because they are not
      counting on free text that is terminated with `\r\n` (either it contains the
      bulk length like string reply or they are typed reply that can not inject free
      text like boolean or number).
      
      The issue only exists on unstable branch, big number reply on Lua script
      was not yet added to any official release.
      b8e82d20
  3. 29 Nov, 2021 4 commits
    • Binbin's avatar
      Fix CLIENT KILL kill all clients with id 0 (#9853) · 3119a3ae
      Binbin authored
      
      
      * Fix CLIENT KILL kill all clients with id 0 or with skipme
      CLIENT KILL with ID argument should only kill the client with the provided ID. In old code, 
      CLIENT KILL with id 0 will kill all the connected clients.
      Co-authored-by: default avatarOfir Luzon <ofirluzon@gmail.com>
      3119a3ae
    • OfirMos's avatar
      fixed mem leak on rdb load error (#9860) · 9f9c7857
      OfirMos authored
      a rare case of short read that can happen when breaking the master-replica
      connection on diskless load mode,
      9f9c7857
    • Binbin's avatar
      Add REDIS_CFLAGS='-Werror' to CI tests (#9828) · 980bb3ae
      Binbin authored
      Update CI so that warnings cause build failures.
      
      Also fix a warning in `test-sanitizer-address`:
      ```
      In function ‘strncpy’,
         inlined from ‘clusterUpdateMyselfIp’ at cluster.c:545:13:
      
      /usr/include/x86_64-linux-gnu/bits/string_fortified.h:106:10:
      error: ‘__builtin_strncpy’ specified bound 46 equals destination size [-Werror=stringop-truncation]
      
        106 |   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
            |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      cc1: all warnings being treated as errors
      ```
      980bb3ae
    • sundb's avatar
      Fix abnormal compression due to out-of-control recompress (#9849) · 494ee2f1
      sundb authored
      This pr is following #9779 .
      
      ## Describe of feature
      Now when we turn on the `list-compress-depth` configuration, the list will compress
      the ziplist between `[list-compress-depth, -list-compress-depth]`.
      When we need to use the compressed data, we will first decompress it, then use it,
      and finally compress it again.
      It's controlled by `quicklistNode->recompress`, which is designed to avoid the need to
      re-traverse the entire quicklist for compression after each decompression, we only need
      to recompress the quicklsitNode being used.
      In order to ensure the correctness of recompressing, we should normally let
      quicklistDecompressNodeForUse and quicklistCompress appear in pairs, otherwise,
      it may lead to the head and tail being compressed or the middle ziplist not being
      compressed correctly, which is exactly the problem this pr needs to solve.
      
      ## Solution
      1. Reset `quicklistIter` after insert and replace.
          The quicklist node will be compressed in `quicklistInsertAfter`, `quicklistInsertBefore`,
         `quicklistReplaceAtIndex`, so we can safely reset the quicklistIter to avoid it being used again
      2. `quicklistIndex` will return an iterator that can be used to recompress the current node after use.
          
      ## Test
      1. In the `Stress Tester for #3343-Similar Errors` test, when the server crashes or when
         `valgrind` or `asan` error is detected, print violating commands.
      2. Add a crash test due to wrongly recompressing after `lrem`.
      3. Remove `insert before with 0 elements` and `insert after with 0 elements`,
         Now we forbid any operation on an NULL quicklistIter.
      494ee2f1
  4. 28 Nov, 2021 3 commits
    • Meir Shpilraien (Spielrein)'s avatar
      Clean Lua stack before parsing call reply to avoid crash on a call with many arguments (#9809) · 6b0b04f1
      Meir Shpilraien (Spielrein) authored
      This commit 0f8b634c (CVE-2021-32626 released in 6.2.6, 6.0.16, 5.0.14)
      fixes an invalid memory write issue by using `lua_checkstack` API to make
      sure the Lua stack is not overflow. This fix was added on 3 places:
      1. `luaReplyToRedisReply`
      2. `ldbRedis`
      3. `redisProtocolToLuaType`
      
      On the first 2 functions, `lua_checkstack` is handled gracefully while the
      last is handled with an assert and a statement that this situation can
      not happened (only with misbehave module):
      
      > the Redis reply might be deep enough to explode the LUA stack (notice
      that currently there is no such command in Redis that returns such a nested
      reply, but modules might do it)
      
      The issue that was discovered is that user arguments is also considered part
      of the stack, and so the following script (for example) make the assertion reachable:
      ```
      local a = {}
      for i=1,7999 do
          a[i] = 1
      end 
      return redis.call("lpush", "l", unpack(a))
      ```
      
      This is a regression because such a script would have worked before and now
      its crashing Redis. The solution is to clear the function arguments from the Lua
      stack which makes the original assumption true and the assertion unreachable.
      6b0b04f1
    • Viktor Söderqvist's avatar
      Sort out the mess around writable replicas and lookupKeyRead/Write (#9572) · acf3495e
      Viktor Söderqvist authored
      Writable replicas now no longer use the values of expired keys. Expired keys are
      deleted when lookupKeyWrite() is used, even on a writable replica. Previously,
      writable replicas could use the value of an expired key in write commands such
      as INCR, SUNIONSTORE, etc..
      
      This commit also sorts out the mess around the functions lookupKeyRead() and
      lookupKeyWrite() so they now indicate what we intend to do with the key and
      are not affected by the command calling them.
      
      Multi-key commands like SUNIONSTORE, ZUNIONSTORE, COPY and SORT with the
      store option now use lookupKeyRead() for the keys they're reading from (which will
      not allow reading from logically expired keys).
      
      This commit also fixes a bug where PFCOUNT could return a value of an
      expired key.
      
      Test modules commands have their readonly and write flags updated to correctly
      reflect their lookups for reading or writing. Modules are not required to
      correctly reflect this in their command flags, but this change is made for
      consistency since the tests serve as usage examples.
      
      Fixes #6842. Fixes #7475.
      acf3495e
    • sundb's avatar
      Fix COMMAND GETKEYS on LCS (#9852) · 4d870078
      sundb authored
      Remove lcsGetKeys to clean up the remaining STRALGO after #9733.
      i.e. it still used a getkeys_proc which was still looking for the KEYS or STRINGS arguments
      4d870078
  5. 27 Nov, 2021 1 commit
  6. 25 Nov, 2021 1 commit
    • uriyage's avatar
      Do not watch keys for dirty client (#9829) · fa48fb2d
      uriyage authored
      
      
      Currently, the watching clients are marked as dirty when a watched
      key is touched, but we continue watching the keys for no reason.
      Then, when the same key is touched again, we iterate again on the
      watching clients list and mark all clients as dirty again.
      Only when the exec/unwatch command is issued will the client be
      removed from the key->watching_clients list. The same applies when
      a dirty client calls the WATCH command. The key will be added to be
      watched by the client even if it has no effect.
      
      In the field, no performance degradation was observed as a result of the
      current implementation; it is merely a cleanup with possible memory and
      performance gains in some situations.
      Co-authored-by: default avatarOran Agra <oran@redislabs.com>
      fa48fb2d
  7. 24 Nov, 2021 2 commits
    • Pavel Melkozerov's avatar
      fix fob bad log messages in rdbSave (#9842) (#9843) · 9630ded3
      Pavel Melkozerov authored
      
      
      logs message prints wrong file is failed to open temporary file
      logs have error occurred in getcwd (uses same errno to report error)
      Co-authored-by: default avatarPavel Melkozerov <pavel.melkozerov@nokia.com>
      9630ded3
    • sundb's avatar
      Replace ziplist with listpack in quicklist (#9740) · 45129059
      sundb authored
      
      
      Part three of implementing #8702, following #8887 and #9366 .
      
      ## Description of the feature
      1. Replace the ziplist container of quicklist with listpack.
      2. Convert existing quicklist ziplists on RDB loading time. an O(n) operation.
      
      ## Interface changes
      1. New `list-max-listpack-size` config is an alias for `list-max-ziplist-size`.
      2. Replace `debug ziplist` command with `debug listpack`.
      
      ## Internal changes
      1. Add `lpMerge` to merge two listpacks . (same as `ziplistMerge`)
      2. Add `lpRepr` to print info of listpack which is used in debugCommand and `quicklistRepr`. (same as `ziplistRepr`)
      3. Replace `QUICKLIST_NODE_CONTAINER_ZIPLIST` with `QUICKLIST_NODE_CONTAINER_PACKED`(following #9357 ).
          It represent that a quicklistNode is a packed node, as opposed to a plain node.
      4. Remove `createZiplistObject` method, which is never used.
      5. Calculate listpack entry size using overhead overestimation in `quicklistAllowInsert`.
          We prefer an overestimation, which would at worse lead to a few bytes below the lowest limit of 4k.
      
      ## Improvements
      1. Calling `lpShrinkToFit` after converting Ziplist to listpack, which was missed at #9366.
      2. Optimize `quicklistAppendPlainNode` to avoid memcpy data.
      
      ## Bugfix
      1. Fix crash in `quicklistRepr` when ziplist is compressed, introduced from #9366.
      
      ## Test
      1. Add unittest for `lpMerge`.
      2. Modify the old quicklist ziplist corrupt dump test.
      Co-authored-by: default avatarOran Agra <oran@redislabs.com>
      45129059
  8. 23 Nov, 2021 2 commits
    • Oran Agra's avatar
      fix invalid read on corrupt ziplist (#9831) · a3a01429
      Oran Agra authored
      If the last bytes in ziplist are corrupt and we decode from tail to head,
      we may reach slightly outside the ziplist.
      a3a01429
    • guybe7's avatar
      QUIT is a command, HOST: and POST are not (#9798) · b161cff5
      guybe7 authored
      Some people complain that QUIT is missing from help/command table.
      Not appearing in COMMAND command, command stats, ACL, etc.
      and instead, there's a hack in processCommand with a comment that looks outdated.
      Note that it is [documented](https://redis.io/commands/quit)
      
      At the same time, HOST: and POST are there in the command table although these are not real commands.
      They would appear in the COMMAND command, and even in commandstats.
      
      Other changes:
      1. Initialize the static logged_time static var in securityWarningCommand
      2. add `no-auth` flag to RESET so it can always be executed.
      b161cff5
  9. 22 Nov, 2021 1 commit
    • Oran Agra's avatar
      Fix invalid access in lpFind on corrupted listpack (#9819) · f07dedf7
      Oran Agra authored
      Issue found by corrupt-dump-fuzzer test with ASAN.
      The problem was that lpSkip and lpGetWithSize could read the next listpack entry without validating that it's in range.
      Similarly even the memcmp in lpFind could do that and possibly crash on segfault and now they'll crash on assert first.
      
      The naive fix of using lpAssertValidEntry every time, resulted in 30% degradation in the lpFind benchmark of the unit test.
      The final fix with the condition at the bottom has no performance implications.
      f07dedf7
  10. 21 Nov, 2021 3 commits
    • Oran Agra's avatar
      Fix false positive leak reported by GCC ASAN (#9816) · 183b90a6
      Oran Agra authored
      Leak found by the corrupt-dump-fuzzer when using GCC ASAN, which seems
      to falsely report leaks on pointers kept only on the stack when calling exit.
      Instead we now use _exit on panic / assert to skip these leak checks.
      
      Additionally, check for sanitizer warnings in the corrupt-dump-fuzzer between iterations,
      so that when something is found we know which test to relate it too (and it prints reproduction command list)
      183b90a6
    • Yossi Gottlieb's avatar
      Fix occasional RM_Call() crashes. (#9805) · fd0ca747
      Yossi Gottlieb authored
      With dynamically growing argc (#9528), it is necessary to initialize
      argv_len. Normally createClient() handles that, but in the case of a
      module shared_client, this needs to be done explicitly.
      
      This also addresses an issue with rewriteClientCommandArgument() which
      doesn't properly handle the case where the new element extends beyond
      argc but not beyond argv_len.
      fd0ca747
    • Oran Agra's avatar
      Prevent LCS from allocating temp memory over proto-max-bulk-len (#9817) · 14176484
      Oran Agra authored
      LCS can allocate immense amount of memory (sizes of two inputs multiplied by each other).
      In the past this caused some possible security issues due to overflows, which we solved
      and also added use of `trymalloc` to return "Insufficient memory" instead of OOM panic zmalloc.
      
      But in case overcommit is enabled, it could be that we won't get the OOM panic, and zmalloc
      will succeed, and then we can get OOM killed by the kernel.
      
      The solution here is to prevent LCS from allocating transient memory that's bigger than
      `proto-max-bulk-len` config.
      This config is not directly related to transient memory, but using a hard coded value ad well as
      introducing a specific config seems wrong.
      
      This comes to solve an error in the corrupt-dump-fuzzer test that started in the daily CI see #9799
      14176484
  11. 20 Nov, 2021 1 commit
  12. 19 Nov, 2021 1 commit
  13. 18 Nov, 2021 6 commits
    • Wen Hui's avatar
      75d50e5d
    • perryitay's avatar
      Fix crashes when list-compress-depth is used. (#9779) · 0c10f0e1
      perryitay authored
      Recently we started using list-compress-depth in tests (was completely untested till now).
      Turns this triggered test failures with the external mode, since the tests left the setting enabled
      and then it was used in other tests (specifically the fuzzer named "Stress tester for #3343-alike bugs").
      
      This PR fixes the issue of the `recompress` flag being left set by mistake, which caused the code to
      later to compress the head or tail nodes (which should never be compressed)
      
      The solution is to reset the recompress flag when it should have been (when it was decided not to compress).
      
      Additionally we're adding some assertions and improve the tests so in order to catch other similar bugs.
      0c10f0e1
    • Eduardo Semprebon's avatar
      Reject PING with MASTERDOWN when replica-serve-stale-data=no (#9757) · 1a255e31
      Eduardo Semprebon authored
      Currently PING returns different status when server is not serving data,
      for example when `LOADING` or `BUSY`.
      But same was not true for `MASTERDOWN`
      This commit makes PING reply with `MASTERDOWN` when
      replica-serve-stale-data=no and link is MASTER is down.
      1a255e31
    • guybe7's avatar
      Obliterate STRALGO! add LCS (which only works on keys) (#9799) · af748988
      guybe7 authored
      Drop the STRALGO command, now LCS is a command of its own and it only works on keys (not input strings).
      The motivation is that STRALGO's syntax was really messed-up...
      - assumes all (future) string algorithms will take similar arguments
      - mixes command that takes keys and one that doesn't in the same command.
      - make it nearly impossible to expose the right key spec in COMMAND INFO (issues cluster clients)
      - hard for cluster clients to determine the key names (firstkey, lastkey, etc)
      - hard for ACL / flags (is it a read command?)
      
      This is a breaking change.
      af748988
    • Binbin's avatar
      Fixes ZPOPMIN/ZPOPMAX wrong replies when count is 0 with non-zset (#9711) · 91e77a0c
      Binbin authored
      Moves ZPOP ... 0 fast exit path after type check to reply with
      WRONGTYPE. In the past it will return an empty array.
      
      Also now count is not allowed to be negative.
      
      see #9680
      
      before:
      ```
      127.0.0.1:6379> set zset str
      OK
      127.0.0.1:6379> zpopmin zset 0
      (empty array)
      127.0.0.1:6379> zpopmin zset -1
      (empty array)
      ```
      
      after:
      ```
      127.0.0.1:6379> set zset str
      OK
      127.0.0.1:6379> zpopmin zset 0
      (error) WRONGTYPE Operation against a key holding the wrong kind of value
      127.0.0.1:6379> zpopmin zset -1
      (error) ERR value is out of range, must be positive
      ```
      91e77a0c
    • Madelyn Olson's avatar
      Unpause clients earlier during manual cluster failover (#9676) · 32215e78
      Madelyn Olson authored
      Unpause clients after manual failover ends instead of the timed offset
      32215e78
  14. 16 Nov, 2021 5 commits
    • Axlgrep's avatar
      optimze the judgment logic of use_pattern in scanGenericCommand() (#9789) · 5f8ee381
      Axlgrep authored
      in `scan 0 match ""` case,  pat is empty sds(patlen is 0), I don't think should access the
      first character directly in this case(even though the first character is ' \0 '), for the
      code readability, I switch the two positions of judgment logic. 
      5f8ee381
    • harleyliao's avatar
      set aof rewrite status err, when fork fail (#5606) · 992a610f
      harleyliao authored
      when aof rewrite is failed by fork(), It'll be indicated by aof_last_bgrewrite_status
      INFO field, same as when the fork child fails later on.
      992a610f
    • sundb's avatar
      Change lzf to handle values larger than UINT32_MAX (#9776) · 985430b4
      sundb authored
      Redis supports inserting data over 4GB into string (and recently for lists too, see #9357),
      But LZF compression used in RDB files (see `rdbcompression` config), and in quicklist
      (see `list-compress-depth` config) does not support compress/decompress data over
      UINT32_MAX, which will result in corrupting the rdb after compression.
      
      Internal changes:
      1. Modify the `unsigned int` parameter of `lzf_compress/lzf_decompress` to `size_t`.
      2. Modify the variable types in `lzf_compress` involving offsets and lengths to `size_t`.
      3. Set LZF_USE_OFFSETS to 0.
          When LZF_USE_OFFSETS is 1, lzf store offset into `LZF_HSLOT`(32bit). 
          Even in 64-bit, `LZF_USE_OFFSETS` defaults to 1, because lzf assumes that it only
          compresses and decompresses data smaller than UINT32_MAX.
          But now we need to make lzf support 64-bit, turning on `LZF_USE_OFFSETS` will make
          it impossible to store 64-bit offsets or pointers.
          BTW, disable LZF_USE_OFFSETS also brings a few performance improvements.
      
      Tests:
      1. Add test for compress/decompress string large than UINT32_MAX.
      2. Add unittest for compress/decompress quicklistNode.
      985430b4
    • sundb's avatar
      Add --large-memory flag for REDIS_TEST to enable tests that consume more than 100mb (#9784) · e725d737
      sundb authored
      This is a preparation step in order to add a new test in quicklist.c see #9776
      e725d737
    • guoxiang1996's avatar
      insufficient size for cached client flags in call() (#9783) · aba70df4
      guoxiang1996 authored
      The client flags is a 64 bit integer, but the temporary cached value on the stack of call() is 32 bit.
      luckily this doesn't lead to any bugs since the only flags used against this variables are below 32 bit.
      aba70df4
  15. 11 Nov, 2021 1 commit
    • Ozan Tezcan's avatar
      Add sanitizer support and clean up sanitizer findings (#9601) · b91d8b28
      Ozan Tezcan authored
      - Added sanitizer support. `address`, `undefined` and `thread` sanitizers are available.  
      - To build Redis with desired sanitizer : `make SANITIZER=undefined`
      - There were some sanitizer findings, cleaned up codebase
      - Added tests with address and undefined behavior sanitizers to daily CI.
      - Added tests with address sanitizer to the per-PR CI (smoke out mem leaks sooner).
      
      Basically, there are three types of issues : 
      
      **1- Unaligned load/store** : Most probably, this issue may cause a crash on a platform that
      does not support unaligned access. Redis does unaligned access only on supported platforms.
      
      **2- Signed integer overflow.** Although, signed overflow issue can be problematic time to time
      and change how compiler generates code, current findings mostly about signed shift or simple
      addition overflow. For most platforms Redis can be compiled for, this wouldn't cause any issue
      as far as I can tell (checked generated code on godbolt.org).
      
       **3 -Minor leak** (redis-cli), **use-after-free**(just before calling exit());
      
      UB means nothing guaranteed and risky to reason about program behavior but I don't think any
      of the fixes here worth backporting. As sanitizers are now part of the CI, preventing new issues
      will be the real benefit. 
      b91d8b28
  16. 09 Nov, 2021 1 commit