Redis validates floating-point increments before creating a hash
Redis 7.0.11 fixes an authenticated denial-of-service flaw in HINCRBYFLOAT. The patch moves nonfinite input rejection ahead of a helper that can create database state.
A command that returns an error can still have changed state before reaching that error. Redis's April HINCRBYFLOAT fix addresses that ordering problem by rejecting a nonfinite increment before calling a helper that can create a hash. The 7.0 release notes date Redis 7.0.11 to April 17 and identify the change as a security fix for CVE-2023-28856.
The advisory published April 18 describes authenticated users creating an invalid hash field that may later crash Redis when accessed. It names 7.0.11, 6.2.12, and 6.0.19 as patched versions. The delayed access is part of the reported impact, so an error response to the initiating command should not be treated as sufficient evidence that stored state remained valid.
I have used Redis since its early releases, and I value the directness of its data commands. That makes their failure behavior particularly important. An application often treats a rejected arithmetic operation as having no useful effect, while the implementation must make that expectation true at each state-changing step.
Lookup can also create an object
The 7.0.10 implementation begins by parsing the increment into a long double. It then calls hashTypeLookupWriteOrCreate(). The helper's role is broader than finding an existing value, since the command needs a hash object when the target key does not yet exist.
After obtaining that object, the command reads the field's existing value. A string representation must parse as a floating-point value, while an integer representation can be converted directly. A missing field supplies an initial value of zero. Only after that lookup and initialization does the command add the requested increment.
The older implementation already checks the computed result with isnan() and isinf(). It returns an error if the increment would produce NaN or infinity. That check protects the result, but its position means the lookup-or-create helper has already run.
The patch in pull request 11149 adds another check immediately after parsing the increment. It rejects NaN or infinity before obtaining the writable hash object. The result check remains in place, because finite operands can still require validation of their computed result.
These checks answer different questions. One establishes that the supplied increment is acceptable before object creation. The other establishes that the arithmetic result is acceptable before storing it. Treating either as redundant would ignore the different operations between their positions in the command.
The regression test checks the absence of a key
The patch's test invokes HINCRBYFLOAT against a key intended to be absent, using a positive-infinity increment. It expects the new input error and then checks that the key does not exist. That second assertion is the significant addition to the command's observable failure contract.
Checking only the returned error would miss the object-lifecycle problem. A server can reject the final arithmetic result after allocating and registering an object. A test that also inspects existence distinguishes an early rejection from a failure that leaves newly created state behind.
The test is also narrower than a complete demonstration of every consequence described by the advisory. It verifies the behavior of this rejected operation on a missing key. The advisory establishes the broader security impact. Keeping those sources distinct avoids inventing an exact crash sequence that the short patch does not document.
The 7.0.11 source contains the early input check. Its order is explicit, with parsing followed by nonfinite rejection and then writable hash lookup. That is the concrete implementation difference an application depends on when sending a request that cannot produce an accepted value.
Successful arithmetic has a separate replication decision
The same source shows how a successful floating-point increment propagates. Redis calculates the new value and stores its formatted representation. It then rewrites the command as an HSET carrying that final value for replication and append-only persistence.
The code comment explains the reason. Repeating floating-point arithmetic elsewhere can expose differences in precision or formatting. Propagating the resulting value avoids asking a replica or an AOF restart to independently derive the same representation from the original increment.
This is a separate correctness decision from early validation. The command first needs to reject unacceptable inputs without leaving invalid state. For accepted operations, it also needs to preserve the chosen result when the operation reaches other execution contexts. Both concerns are visible within the same small function.
That propagation path also explains why the arithmetic result check remains useful after the new input check. An acceptable increment does not remove the need to validate the result that will become stored and propagated data. The two validations protect different stages of that transition.
Authentication does not validate arithmetic
The advisory identifies an authenticated attack path. A credential permits access according to policy, but does not establish that every supplied floating-point value is suitable for a particular operation. The server still owns validation of the command's internal state transitions.
The reported impact is availability through later access to invalid state. It should not be expanded into a claim of arbitrary code execution or disclosure of stored values. The primary advisory supplies a specific consequence, and the implementation supplies a specific repair to the path that creates the problematic state.
Redis 7.0.11 also contains separate corrections, including a missing AOF synchronization in rare cases and disconnecting pub/sub subscribers when all-channel permission is revoked. Those entries do not share the floating-point mechanism. They reflect other state transitions that the release changes under their own conditions.
I prefer the test's concrete postcondition to a vague claim that input is now handled safely. The rejected nonfinite increment leaves the previously absent key absent. The implementation achieves that by moving a necessary validation before a helper with side effects, while retaining validation of the successful arithmetic result and its explicit propagation as a final value.