Redis fixes a buffer resize error in its string library
Redis 7.2.4 fixes CVE-2023-41056. The patch shows why an allocator-size optimization must respect the string representation's own layout rules.
An allocator reporting the expected block size does not prove that a string's internal representation has the layout its metadata describes. Redis 7.2.4 fixes a buffer-resizing error at that boundary. The January 9 release addresses CVE-2023-41056, which can lead to a heap overflow and potential remote code execution when buffer sizes are accounted for incorrectly.
The Redis 7.2 release notes classify the update as a security release. Its notes pinned to 7.2.4 preserve the account available with this version. An important engineering detail lies in the relationship between a resizable allocation and Redis's dynamic-string headers. A shortcut that can avoid an unnecessary allocation must not avoid a required change in representation.
I have used Redis since its release, and I still value the directness of its data model. That makes the implementation below the command interface worth understanding. Familiar commands eventually depend on memory-accounting code, and a defect there can have consequences much larger than a failed operation on one key.
What the advisory establishes
The project's security advisory, published on January 9, identifies incorrect resizing and buffer-size accounting as the problem. It states that Redis 7.0.9 and newer are affected, including the 7.2 series, and identifies 7.0.15 and 7.2.4 as fixed versions. Those are the release boundaries relevant to this disclosure.
The advisory describes potential remote code execution, which is a consequence to take seriously without treating it as proof of a published exploit against every deployment. Its account is deliberately short. It does not provide a complete application-level sequence for triggering the defect. The source change supports a detailed explanation of the internal correction without inventing that missing sequence.
The affected range is also more specific than saying every Redis version has this problem. A version number alone needs to be interpreted against the disclosed range and the deployed package. For an operator following the upstream release line, the fixed patch versions are explicit. A deployment using another distributor's build needs to establish what code that build includes.
This is a memory-safety correction inside the server. Application-level handling of Redis errors cannot repair the server's buffer metadata. The useful distinction is between recovering from a rejected command and correcting an implementation that can describe memory incorrectly. The patch addresses the latter at the point where resizing occurs.
Redis strings carry metadata before the bytes
The 7.2.4 dynamic-string implementation represents strings with a header preceding their character data. The header type determines how the implementation stores information such as length and capacity. Several header forms exist so a short string does not need the same metadata width as a much larger one.
A string pointer refers to the character data, while helper functions recover the allocation and its header using the recorded type. The implementation also retains a terminating zero byte even though the string is binary safe and its length comes from metadata. The allocation therefore has several related sizes, including the header, the data capacity and the terminator.
Resizing can change more than the number of bytes reserved after the string. A sufficiently different target size can make another header representation appropriate. Moving between header layouts changes where the character data sits relative to the beginning of the allocation. Keeping a consistent pointer and type field is part of the resize operation's correctness.
The resize function can also truncate the logical length when the requested size is smaller. It calculates the appropriate header type and decides whether it can use reallocation while retaining the old header layout. In other cases, it allocates a new block, copies the string to the position after the new header, frees the old block, and updates the type information.
These steps explain why capacity is more than an allocator fact. Redis needs the capacity it records to be consistent with the layout through which later code accesses the string. A block with enough total bytes does not automatically have the correct relationship between its base pointer, header and character data.
The function's comments explicitly permit retaining some larger headers when shrinking. Avoiding a header conversion can be a valid optimization when the retained header can still represent the requested size with acceptable overhead. The code therefore does not require every resize to select the smallest possible representation. It requires the representation actually retained to remain consistent with the allocation and recorded metadata.
Another parameter records whether the string is expected to grow again. In that case, the code avoids its smallest header form because that form is unsuitable for tracking spare capacity during repeated appends. This is a separate policy from the allocator shortcut, and it helps explain why resizing cannot be reduced to a single requested-byte calculation. The library chooses a representation appropriate to later operations as well as the immediate size.
The function returns the resulting string pointer after all of this work. Callers must use that returned pointer because allocation or header changes can move the character data. The memory-management abstraction includes both the bytes and the metadata needed for subsequent access.
The allocator shortcut belonged inside one branch
The difference between 7.2.3 and 7.2.4 shows the correction in sdsResize. The function contains a jemalloc-specific optimization. It compares the expected allocation size for the request with the allocator's reported size for the existing block. If they are already the same, calling reallocation can be avoided in the path where retaining the layout is appropriate.
Before the fix, that optimization's decision was calculated outside the branch that determines whether reallocation can preserve the existing representation. It also guarded the alternative allocation-and-copy branch. As a result, the allocator-size shortcut could suppress work needed when changing representation, even though equality of allocation sizes did not justify retaining that representation.
The fixed version nests the optimization inside the use_realloc branch. When reallocation is appropriate, it can still skip an unnecessary allocator call. When the other branch is required, the function allocates and copies without using that shortcut to suppress the operation. The code then writes the terminator and records the updated logical length and allocation size.
This is a control-flow correction with a concrete invariant. The condition that permits retaining an allocation's layout must hold before the code considers whether the allocator call itself is redundant. The patch preserves the optimization where its assumptions apply and removes its influence over the path that must construct another layout.
I find this kind of patch more informative than a generic warning about unsafe languages. The error concerns two different notions of equivalence. Blocks can occupy the same allocator size class while requiring different internal string layouts. The fixed branch ordering makes the representation decision authoritative before the allocation shortcut is considered.
The release also addresses mixed cluster operation
The same 7.2.4 notes include fixes for cluster-command crashes in clusters mixing 7.0 and 7.2 nodes. They also identify a slot-ownership correction when deleting a slot from a node and an atomicity issue in a module key event. These are separate fixes, and the security correction should not be presented as their common cause.
The mixed-version item is relevant because the period during an upgrade is itself an operating state. Software can encounter a combination of versions even when the intended final deployment is uniform. A patch release that fixes a problem in that combination affects the procedure used to reach the desired version, not merely the steady state afterward.
Slot ownership and module-event atomicity concern another pair of boundaries. Cluster metadata must accurately describe which node owns a slot. Module callbacks must observe the event behaviour their API defines. The release notes identify the corrections without supplying enough detail to assign every affected application a specific failure scenario, so those summaries should retain their stated scope.
The security fix remains the reason this release carries security urgency. Its source shows the particular work required to keep memory metadata consistent during resizing. The surrounding correctness fixes address cluster and module behaviour independently. For a Redis 7.2 deployment following upstream patches, 7.2.4 is the release that combines those changes with the disclosed correction to buffer resizing.