From 6ea4e2c949c7fa50b85ff59647ee5e3f95e97037 Mon Sep 17 00:00:00 2001 From: "debing.sun" Date: Tue, 28 Oct 2025 21:35:03 +0800 Subject: [PATCH] Fix race condition for lookupCommand (#14466) Fixes data race where main thread modifies pauserehash in dictNext while IO thread reads `useStoredKeyApi()` in `lookupCommand()->dictFind()` path. ASAN detected overlapping memory access at same byte offset causing race condition. When bit fields are adjacent in a struct, modifying one bit field requires a read-modify-write operation on the entire memory unit, which can cause race conditions with concurrent access to other bit fields in the same unit. This was introduced by https://github.com/redis/redis/pull/13696, although it was changed by https://github.com/redis/redis/pull/14440, but this issue still exist. The fix moves `useStoredKeyApi` to share a memory word with `pauseAutoResize` instead. Since `pauseAutoResize` is never modified for `server.commands`, this eliminates the race condition while maintaining memory efficiency through bit field packing. Reproduce step: ``` make SANITIZER=thread ./runtest --tsan --config io-threads 4 --accurate --verbose --dump-logs --single unit/obuf-limits --loop --stop ``` failed CI: https://github.com/redis/redis/actions/runs/18803219305/job/53653572710 --------- Co-authored-by: Moti Cohen --- src/dict.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dict.h b/src/dict.h index 27b37233c..f192f9c6b 100644 --- a/src/dict.h +++ b/src/dict.h @@ -127,12 +127,14 @@ struct dict { long rehashidx; /* rehashing not in progress if rehashidx == -1 */ - /* Keep small vars at end for optimal (minimal) struct padding */ - unsigned pauserehash : 15; /* If >0 rehashing is paused */ + /* Note: pauserehash is a full unsigned so iterator increments + * don't perform RMW on the same storage unit as other bitfields. */ + unsigned pauserehash; /* If >0 rehashing is paused */ - unsigned useStoredKeyApi : 1; /* See comment of storedHashFunction above */ + /* Keep small vars at end for optimal (minimal) struct padding */ signed char ht_size_exp[2]; /* exponent of size. (size = 1<0 automatic resizing is disallowed (<0 indicates coding error) */ + signed pauseAutoResize: 15; /* If >0 automatic resizing is disallowed (<0 indicates coding error) */ + unsigned useStoredKeyApi: 1; /* See comment of storedHashFunction above */ void *metadata[]; };