Skip to content

fix(hash): Untrack hash with volatile fields when it is overwritten #3003

Merged
ranshid merged 6 commits into
valkey-io:unstablefrom
frostzt:hash_untrack_volatile_items
Jan 4, 2026
Merged

fix(hash): Untrack hash with volatile fields when it is overwritten #3003
ranshid merged 6 commits into
valkey-io:unstablefrom
frostzt:hash_untrack_volatile_items

Conversation

@frostzt

@frostzt frostzt commented Jan 4, 2026

Copy link
Copy Markdown
Contributor

In dbSetValue if the robj is a hash with volatile fields we need to clean it up. It causes two core problems:

  1. Type Confusion: Allows converting from hash to string with keys_with_volatile_items tracked
  2. Tracked Items with dangling pointer: Converting from hash to ie. a string breaks this as keys_with_volatile_items is not cleaned up.

Check #3000

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
@frostzt

frostzt commented Jan 4, 2026

Copy link
Copy Markdown
Contributor Author

Ain't gonna lie I am not sure about this test here! Is this related to this or is this something else?

@codecov

codecov Bot commented Jan 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.02%. Comparing base (50667e0) to head (da8d612).
⚠️ Report is 5 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #3003      +/-   ##
============================================
- Coverage     74.29%   74.02%   -0.27%     
============================================
  Files           129      129              
  Lines         70895    70899       +4     
============================================
- Hits          52670    52485     -185     
- Misses        18225    18414     +189     
Files with missing lines Coverage Δ
src/db.c 94.17% <100.00%> (+0.01%) ⬆️

... and 25 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread tests/unit/type/hash.tcl Outdated
Comment thread tests/unit/type/hash.tcl Outdated
Comment thread tests/unit/type/hash.tcl Outdated
Comment thread tests/unit/type/hash.tcl Outdated
Comment thread tests/unit/type/hash.tcl Outdated
Comment thread tests/unit/type/hash.tcl Outdated
Comment thread tests/unit/type/hash.tcl Outdated
Comment thread tests/unit/type/hash.tcl Outdated
@ranshid ranshid moved this to To be backported in Valkey 9.0 Jan 4, 2026
@ranshid ranshid added bug Something isn't working release-notes This issue should get a line item in the release notes labels Jan 4, 2026
@ranshid ranshid changed the title fix(hash): clean volatile fields if the oldobj is a hash fix(hash): Untrack hash with volatile fields when it is overwritten Jan 4, 2026
@ranshid

ranshid commented Jan 4, 2026

Copy link
Copy Markdown
Member

Ain't gonna lie I am not sure about this test here! Is this related to this or is this something else?

not related to #3000. this is a "day 1" HFE bug

@ranshid ranshid closed this Jan 4, 2026
@github-project-automation github-project-automation Bot moved this from To be backported to Done in Valkey 9.0 Jan 4, 2026
@ranshid ranshid reopened this Jan 4, 2026
Comment thread tests/unit/type/hash.tcl Outdated
@ranshid ranshid moved this from Done to To be backported in Valkey 9.0 Jan 4, 2026
@ranshid ranshid moved this from Done to Todo in Valkey 9.1 Jan 4, 2026
Signed-off-by: frostzt <aidenfrostbite@gmail.com>

@dvkashapov dvkashapov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure initial suggestion is sufficient but I may be wrong. In case where both old and new are hashes with volatile fields, shouldn't pointer be updated if it changed?

Comment thread src/db.c
serverAssert(expireref != NULL);
*expireref = new;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ranshid After this line, shouldn't we do something like this?

if (old_was_hash_with_volatile) {
    bool new_is_hash_with_volatile = (new->type == OBJ_HASH && hashTypeHasVolatileFields(new));
    if (new_is_hash_with_volatile) {
        int dict_index = getKVStoreIndexForKey(key->ptr);
        hashtable *volatile_items_ht = kvstoreGetHashtable(db->keys_with_volatile_items, dict_index);
        hashtableReplaceReallocatedEntry(volatile_items_ht, old, new);
    } else {
        dbUntrackKeyWithVolatileItems(db, new);
    }
} else {
    dbTrackKeyWithVolatileItems(db, new);
}

where old_was_hash_with_volatile = (old->type == OBJ_HASH && hashTypeHasVolatileFields(old));

@ranshid ranshid Jan 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So theoretically if there is a case were someone will swap the content of a hash object with a different hash object I would say yes. the thing is, I do not think there is anyway this is currently done today.
The thing is that dbSetValue is called from setKey, dbAddInternal and dbReplaceValue
in all cases were the the old value was hash, it will be untracked (as we wanted).

The problem you mention is "what happen in case the new value is also a hash with volatile items?"
I think there is no case currently which allows that, but I would say that we can add a generic support for that by just calling

dbTrackKeyWithVolatileItems(db, new);

Comment thread tests/unit/hashexpire.tcl Outdated
Comment thread tests/unit/hashexpire.tcl Outdated
Signed-off-by: frostzt <aidenfrostbite@gmail.com>
@ranshid

ranshid commented Jan 4, 2026

Copy link
Copy Markdown
Member

@frostzt lets take @dvkashapov advise and make the change instead like:

--- a/src/db.c
+++ b/src/db.c
@@ -370,6 +370,12 @@ static void dbSetValue(serverDb *db, robj *key, robj **valref, int overwrite, vo
             *expireref = new;
         }
     }
+    /* If overwriting a hash object, un-track it from the volatile items tracking if it contains volatile items.*/
+    if (old->type == OBJ_HASH && hashTypeHasVolatileFields(old)) {
+        dbUntrackKeyWithVolatileItems(db, old);
+    }
+    /* If the new object is a hash with volatile items we need to track it again */
+    dbTrackKeyWithVolatileItems(db, new);
     /* For efficiency, let the I/O thread that allocated an object also deallocate it. */
     if (tryOffloadFreeObjToIOThreads(old) == C_OK) {
         /* OK */

@frostzt

frostzt commented Jan 4, 2026

Copy link
Copy Markdown
Contributor Author

@frostzt lets take @dvkashapov advise and make the change instead like:

--- a/src/db.c
+++ b/src/db.c
@@ -370,6 +370,12 @@ static void dbSetValue(serverDb *db, robj *key, robj **valref, int overwrite, vo
             *expireref = new;
         }
     }
+    /* If overwriting a hash object, un-track it from the volatile items tracking if it contains volatile items.*/
+    if (old->type == OBJ_HASH && hashTypeHasVolatileFields(old)) {
+        dbUntrackKeyWithVolatileItems(db, old);
+    }
+    /* If the new object is a hash with volatile items we need to track it again */
+    dbTrackKeyWithVolatileItems(db, new);
     /* For efficiency, let the I/O thread that allocated an object also deallocate it. */
     if (tryOffloadFreeObjToIOThreads(old) == C_OK) {
         /* OK */

@ranshid Awesome just so I get the idea right here, the dbUntrackKeyWithVolatileItems(db, old); handles the case we already know it untracks the volatile items associated with the old object. However the case we now account for is that the new object itself is a hash in this case we need to track the new object's keys instead? My question here is that you also mentioned "currently not possible", now i might be totally off here but just wanna sound dumb now rather be sorry later.

It means that when we do

HSETEX myhash EX 100 FIELDS 1 field1 value1

We can't really create myhash from scratch? With totally different values?

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
Comment thread src/db.c Outdated
dbUntrackKeyWithVolatileItems(db, old);
}
/* If the new object is a hash with volatile items we need to track it again */
dbTrackKeyWithVolatileItems(db, new);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah we can't do this here its uninitialized here!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ranshid should we move this dbTrackKeyWithVolatileItems inside the else...if (expiry... block?

@ranshid ranshid Jan 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no you should move this entire new section to line 379 (373 in the original implementation)

@ranshid

ranshid commented Jan 4, 2026

Copy link
Copy Markdown
Member

@frostzt lets take @dvkashapov advise and make the change instead like:

--- a/src/db.c
+++ b/src/db.c
@@ -370,6 +370,12 @@ static void dbSetValue(serverDb *db, robj *key, robj **valref, int overwrite, vo
             *expireref = new;
         }
     }
+    /* If overwriting a hash object, un-track it from the volatile items tracking if it contains volatile items.*/
+    if (old->type == OBJ_HASH && hashTypeHasVolatileFields(old)) {
+        dbUntrackKeyWithVolatileItems(db, old);
+    }
+    /* If the new object is a hash with volatile items we need to track it again */
+    dbTrackKeyWithVolatileItems(db, new);
     /* For efficiency, let the I/O thread that allocated an object also deallocate it. */
     if (tryOffloadFreeObjToIOThreads(old) == C_OK) {
         /* OK */

@ranshid Awesome just so I get the idea right here, the dbUntrackKeyWithVolatileItems(db, old); handles the case we already know it untracks the volatile items associated with the old object. However the case we now account for is that the new object itself is a hash in this case we need to track the new object's keys instead? My question here is that you also mentioned "currently not possible", now i might be totally off here but just wanna sound dumb now rather be sorry later.

It means that when we do

HSETEX myhash EX 100 FIELDS 1 field1 value1

We can't really create myhash from scratch? With totally different values?

I think there is no easy way you can currently test the case of "in-place replacing a hash with a different hash"
The only case that comes to mind is using the ValkeyModule_ModuleTypeSetValue and write some dedicated module test, But the problematic part is to create a hash object with VM_HashSet since it will also crerate this object in the generic dictionary. You can then try to use the same object in order to replace another object that was initially created with volatile elements. IMO this is too complex once we made the code to work this way.

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
Comment thread src/db.c Outdated
Comment on lines +338 to +341
/* If overwriting a hash object, un-track it from the volatile items tracking if it contains volatile items.*/
if (old->type == OBJ_HASH && hashTypeHasVolatileFields(old)) {
dbUntrackKeyWithVolatileItems(db, old);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this section should be moved before the section you added in line 378

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok now it makes total sense to me, actually when we put this part inside the overwrite I was a little confused as the comment above says that its only when we perform edits!

Signed-off-by: frostzt <aidenfrostbite@gmail.com>

@ranshid ranshid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! Thank you @frostzt and @dvkashapov !

@frostzt

frostzt commented Jan 4, 2026

Copy link
Copy Markdown
Contributor Author

Perfect! Thank you @frostzt and @dvkashapov !

Thanks a lot @ranshid honestly I am more surprised by how much you know this thing internally! Please feel free to mention me to issues or (if you think) features that you feel I can pick up! I am more than eager to contribute to Valkey 😃

Hopefully I can clear our your backlogs maybe 🤣

@ranshid ranshid merged commit e4a3e9f into valkey-io:unstable Jan 4, 2026
24 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Valkey 9.1 Jan 4, 2026
@frostzt frostzt deleted the hash_untrack_volatile_items branch January 4, 2026 18:31
ranshid added a commit that referenced this pull request Jan 5, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in #3003 
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
jdheyburn pushed a commit to jdheyburn/valkey that referenced this pull request Jan 8, 2026
…alkey-io#3003)

In `dbSetValue` if the robj is a hash with volatile fields we need to
clean it up. It causes two core problems:
1. Type Confusion: Allows converting from hash to string with
`keys_with_volatile_items` tracked
2. Tracked Items with dangling pointer: Converting from `hash` to ie. a
string breaks this as `keys_with_volatile_items` is not cleaned up.

Check valkey-io#3000

---------

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
jdheyburn pushed a commit to jdheyburn/valkey that referenced this pull request Jan 8, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in valkey-io#3003 
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
ranshid pushed a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
…alkey-io#3003)

In `dbSetValue` if the robj is a hash with volatile fields we need to
clean it up. It causes two core problems:
1. Type Confusion: Allows converting from hash to string with
`keys_with_volatile_items` tracked
2. Tracked Items with dangling pointer: Converting from `hash` to ie. a
string breaks this as `keys_with_volatile_items` is not cleaned up.

Check valkey-io#3000

---------

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
ranshid added a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in valkey-io#3003 
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
ranshid pushed a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
…alkey-io#3003)

In `dbSetValue` if the robj is a hash with volatile fields we need to
clean it up. It causes two core problems:
1. Type Confusion: Allows converting from hash to string with
`keys_with_volatile_items` tracked
2. Tracked Items with dangling pointer: Converting from `hash` to ie. a
string breaks this as `keys_with_volatile_items` is not cleaned up.

Check valkey-io#3000

---------

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
ranshid added a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in valkey-io#3003
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
@zuiderkwast zuiderkwast moved this from To be backported to 9.0.2 WIP in Valkey 9.0 Jan 28, 2026
ranshid pushed a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
…alkey-io#3003)

In `dbSetValue` if the robj is a hash with volatile fields we need to
clean it up. It causes two core problems:
1. Type Confusion: Allows converting from hash to string with
`keys_with_volatile_items` tracked
2. Tracked Items with dangling pointer: Converting from `hash` to ie. a
string breaks this as `keys_with_volatile_items` is not cleaned up.

Check valkey-io#3000

---------

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
ranshid added a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in valkey-io#3003 
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
ranshid pushed a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
…alkey-io#3003)

In `dbSetValue` if the robj is a hash with volatile fields we need to
clean it up. It causes two core problems:
1. Type Confusion: Allows converting from hash to string with
`keys_with_volatile_items` tracked
2. Tracked Items with dangling pointer: Converting from `hash` to ie. a
string breaks this as `keys_with_volatile_items` is not cleaned up.

Check valkey-io#3000

---------

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
ranshid added a commit to ranshid/valkey that referenced this pull request Jan 28, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in valkey-io#3003
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
ranshid pushed a commit that referenced this pull request Jan 29, 2026
…3003)

In `dbSetValue` if the robj is a hash with volatile fields we need to
clean it up. It causes two core problems:
1. Type Confusion: Allows converting from hash to string with
`keys_with_volatile_items` tracked
2. Tracked Items with dangling pointer: Converting from `hash` to ie. a
string breaks this as `keys_with_volatile_items` is not cleaned up.

Check #3000

---------

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
ranshid added a commit that referenced this pull request Jan 29, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in #3003
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
hpatro pushed a commit to hpatro/valkey that referenced this pull request Mar 5, 2026
…alkey-io#3003)

In `dbSetValue` if the robj is a hash with volatile fields we need to
clean it up. It causes two core problems:
1. Type Confusion: Allows converting from hash to string with
`keys_with_volatile_items` tracked
2. Tracked Items with dangling pointer: Converting from `hash` to ie. a
string breaks this as `keys_with_volatile_items` is not cleaned up.

Check valkey-io#3000

---------

Signed-off-by: frostzt <aidenfrostbite@gmail.com>
Signed-off-by: Harkrishn Patro <bunty.hari@gmail.com>
hpatro pushed a commit to hpatro/valkey that referenced this pull request Mar 5, 2026
In `dbSetValue()` the `old` pointer may be reassigned to point to the
incoming value object which was created without an embedded key, so
calling `dbUntrackKeyWithVolatileItems()` would call `objectGetKey()`
which returns NULL, causing a crash in `hashtableSdsHash()` when trying
to hash the NULL key.

Idea is to assign `old_was_hash_with_volatile` before the swap and use
`new` instead of `old` for untracking when theres no embedded key.

Introduced in valkey-io#3003
Run with NULL ptr dereference:
https://github.com/valkey-io/valkey/actions/runs/20701343184/job/59424029880

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: Harkrishn Patro <bunty.hari@gmail.com>
lmagomes pushed a commit to lmagomes/home-services that referenced this pull request May 12, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [docker.io/valkey/valkey](https://github.com/valkey-io/valkey) | image | patch | `9.0.1` → `9.0.4` |

---

### Release Notes

<details>
<summary>valkey-io/valkey (docker.io/valkey/valkey)</summary>

### [`v9.0.4`](https://github.com/valkey-io/valkey/releases/tag/9.0.4)

[Compare Source](valkey-io/valkey@9.0.3...9.0.4)

Upgrade urgency SECURITY: This release includes security fixes we recommend you
apply as soon as possible.

##### Security fixes

- (CVE-2026-23479) Use-After-Free in unblock client flow
- (CVE-2026-25243) Invalid Memory Access in RESTORE command
- (CVE-2026-23631) Use-after-free when full sync occurs during a yielding Lua/function execution

### [`v9.0.3`](https://github.com/valkey-io/valkey/releases/tag/9.0.3)

[Compare Source](valkey-io/valkey@9.0.2...9.0.3)

##### Valkey 9.0.3

Upgrade urgency SECURITY: This release includes security fixes we recommend you
apply as soon as possible.

##### Security fixes

- (CVE-2025-67733) RESP Protocol Injection via Lua error\_reply
- (CVE-2026-21863) Remote DoS with malformed Valkey Cluster bus message
- (CVE-2026-27623) Reset request type after handling empty requests

##### Bug fixes

- Avoids crash during MODULE UNLOAD when ACL rules reference a module command and subcommand ([#&#8203;3160](valkey-io/valkey#3160))
- Fix server assert on ACL LOAD when current user loses permission to channels ([#&#8203;3182](valkey-io/valkey#3182))
- Fix bug causing no response flush sometimes when IO threads are busy ([#&#8203;3205](valkey-io/valkey#3205))

### [`v9.0.2`](https://github.com/valkey-io/valkey/releases/tag/9.0.2)

[Compare Source](valkey-io/valkey@9.0.1...9.0.2)

Upgrade urgency HIGH: There are critical bugs that may affect a subset of users.

#### Bug fixes

- Avoid memory leak of new argv when HEXPIRE commands target only non-exiting fields ([#&#8203;2973](valkey-io/valkey#2973))
- Fix HINCRBY and HINCRBYFLOAT to update volatile key tracking ([#&#8203;2974](valkey-io/valkey#2974))
- Avoid empty hash object when HSETEX added no fields ([#&#8203;2998](valkey-io/valkey#2998))
- Fix case-sensitive check for the FNX and FXX arguments in HSETEX ([#&#8203;3000](valkey-io/valkey#3000))
- Prevent assertion in active expiration job after a hash with volatile fields is overwritten ([#&#8203;3003](valkey-io/valkey#3003), [#&#8203;3007](valkey-io/valkey#3007))
- Fix HRANDFIELD to return null response when no field could be found ([#&#8203;3022](valkey-io/valkey#3022))
- Fix HEXPIRE to not delete items when validation rules fail and expiration is in the past ([#&#8203;3023](valkey-io/valkey#3023), [#&#8203;3048](valkey-io/valkey#3048))
- Fix how hash is handling overriding of expired fields overwrite ([#&#8203;3060](valkey-io/valkey#3060))
- HSETEX - Always issue keyspace notifications after validation ([#&#8203;3001](valkey-io/valkey#3001))
- Make zero a valid TTL for hash fields during import mode and data loading ([#&#8203;3006](valkey-io/valkey#3006))
- Trigger prepareCommand on argc change in module command filters ([#&#8203;2945](valkey-io/valkey#2945))
- Restrict TTL from being negative and avoid crash in import-mode ([#&#8203;2944](valkey-io/valkey#2944))
- Fix chained replica crash when doing dual channel replication ([#&#8203;2983](valkey-io/valkey#2983))
- Skip slot cache optimization for AOF client to prevent key duplication and data corruption ([#&#8203;3004](valkey-io/valkey#3004))
- Fix used\_memory\_dataset underflow due to miscalculated used\_memory\_overhead ([#&#8203;3005](valkey-io/valkey#3005))
- Avoid duplicate calculations of network-bytes-out in slot stats with copy-avoidance ([#&#8203;3046](valkey-io/valkey#3046))
- Fix XREAD returning error on empty stream with + ID ([#&#8203;2742](valkey-io/valkey#2742))

#### Performance/Efficiency Improvements

- Track reply bytes in I/O threads if commandlog-reply-larger-than is -1 ([#&#8203;3086](valkey-io/valkey#3086), [#&#8203;3126](valkey-io/valkey#3126)).
  This makes it possible to mitigate a performance regression in 9.0.1 caused by the bug fix [#&#8203;2652](valkey-io/valkey#2652).

**Full Changelog**: <valkey-io/valkey@9.0.1...9.0.2>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "before 6am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE2OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working release-notes This issue should get a line item in the release notes

Projects

Status: 9.0.2
Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants