April 23, 2025·6 min read·engineeringvalkeysecuritynetworkingmemory

Valkey limits replies before a client authenticates

Valkey 8.1.1 fixes CVE-2025-21605 by bounding output for clients that have never authenticated. The repair separates command authorization from the memory cost of rejecting commands.

Rejecting an unauthorized command still produces a reply. If a connection keeps sending commands without reading those replies, denying access to database values does not by itself bound the memory retained for the connection. Valkey 8.1.1 addresses that distinction with an output-buffer restriction for clients that have never authenticated. The release announcement identifies the correction as CVE-2025-21605 and recommends applying the security update as soon as possible.

The implementation is more precise than the phrase "unauthenticated clients" initially suggests. The security patch records whether a connection has ever authenticated, separately from whether it is authenticated now. It applies the special limit when authentication is required and the connection has never completed it. A connection which previously authenticated has a different history, even if its current authentication state has been reset.

I default to Valkey where Redis semantics are useful, and this is a repair that matters without changing those semantics. Application authorization and connection resource accounting protect different parts of the service. The patch makes their relationship explicit at the point where replies consume memory.

An error reply consumes server resources

The networking implementation in the released tag checks output-buffer memory before applying the usual client-class limits. For the new case, it compares accounted output memory with REPLY_BUFFER_SIZE_UNAUTHENTICATED_CLIENT, while also checking authentication requirements and connection history. That establishes a separate early condition for limiting an untrusted connection.

The constant in server.h is 1024 bytes. That should be described as a threshold in the output-buffer accounting path, not a promise that an unauthenticated connection consumes only 1024 bytes of process memory. The client structure and other buffers have their own allocations. The security check concerns one particular way a connection can retain an increasing amount of memory.

The distinction matters when interpreting the patch. A threshold on output-buffer accounting does not replace a total connection budget. It prevents this output accumulation from proceeding under the same policy as normal clients, where an unlimited default can be appropriate for established application behavior. The server now uses authentication history to select the stricter treatment.

The mechanism also explains why this is an availability issue before successful authentication. Authorization can prevent a command from reading or changing data while the server still parses the request and prepares an error. If the peer does not consume the response, the server must either retain it or terminate the connection. The fix adds a limit at that resource boundary.

Authentication history is an independent state

The patch centralizes user attachment in a clientSetUser function. It sets the current authentication flag and, upon authentication, records the persistent ever_authenticated flag. The distinction remains when current authentication is removed. Paths involving ACL users, module authentication and replication now use that helper where the patch previously assigned the fields directly.

This makes a state transition carry its own bookkeeping. Merely adding a flag to the structure would be insufficient if some authentication paths failed to set it. Using the helper across those paths keeps the limit's decision consistent with the connection's history. The change is therefore broader than inserting a single size comparison in networking code.

The authentication tests included with the release exercise both relevant histories. One connection has never authenticated and must disconnect when the test forces replies into the accounted list. Another authenticates, resets its state and then receives an authentication error without the same special disconnection. That second case demonstrates why current authentication alone cannot describe the implemented rule.

The test uses a debug facility to force replies into the reply list, making the accounting condition deterministic. It is an implementation test technique, rather than a configuration needed for production protection. The normal server initializes that debug behavior as disabled, and the security check itself applies through ordinary reply-buffer limit handling.

A user reading only the release summary might expect every presently unauthenticated connection to receive identical treatment. The tests establish the narrower contract. Recording that contract accurately matters when diagnosing a connection that remains open after resetting authentication, since such behavior can agree with the patch's intended distinction.

Existing output limits address other client behavior

The 8.1.1 configuration file documents separate output-buffer policies for normal, replica and Pub/Sub clients. A hard threshold disconnects a client immediately when reached. A soft threshold requires the buffer to remain above its configured size for the configured interval. Setting a threshold to zero disables it.

The default normal-client directive is explicit.

client-output-buffer-limit normal 0 0 0

That default helps explain the need for a special rule at authentication time. Normal clients generally request the data they receive, whereas replicas and subscribers can receive ongoing pushed data. The configuration comments already acknowledge that asynchronous clients can request data faster than they read it. The new security check treats never-authenticated connections as a distinct resource-accounting case.

Applying one small limit indiscriminately to every connection would have different application consequences. A client legitimately receiving a large value needs output capacity appropriate to that value and its consumption rate. The patch uses connection history to address the security defect without redefining normal output behavior around the small unauthenticated threshold.

This also illustrates why memory controls need names that identify what they cover. Database values, query buffers and output buffers represent different allocations and different causes of growth. The security correction operates on reply memory. Describing it as a new dataset-size limit would direct an investigation toward the wrong configuration and the wrong workload.

I prefer the explicit state in this fix to an explanation that assumes an authentication error is free. The server has to send that error through the same networking machinery as other replies. Its resource policy needs to account for the period before it trusts the connection.

TLS acceptance needs publication ordering

The release also fixes a crash during TLS handshakes with I/O threads. The small patch in pull request 1955 adds a release memory fence after connAccept and before assigning the completed-I/O state. Its placement documents a boundary between performing connection acceptance and publishing that completion to another thread.

This is separate from the authentication-buffer defect. One change limits retained replies based on client history. The other establishes ordering around an I/O-thread state transition. Both affect the early life of a connection, which is why a server's connection handling requires more scrutiny than checking whether established commands return the right values.

The patch is a useful example of how a short diff can correspond to a significant correctness requirement. Another thread observing completion needs the preceding connection work to have the intended visibility. Counting changed lines says little about the consequence of getting that relationship wrong.

Cluster messages can arrive with obsolete state

Pull request 1777, also included in this release, addresses out-of-order gossip during role changes. Its explanation describes a newly promoted primary whose old message still identifies it as a replica. Another node can learn the new ownership through a different message before receiving that older information.

Processing the obsolete message can then recreate an invalid primary-replica relationship. The repair checks the configuration epoch and stops processing stale information in the relevant path. The documented failure demonstrates why message arrival order cannot stand in for the age of the cluster state described by each message.

The release also repairs consumer-group lag accounting. Deletion tombstones and trimming can make a previously tracked position insufficient to calculate a valid lag. The patch changes the conditions under which the server increments the read counter or treats its value as unknown. An unknown count communicates a different state from a plausible but incorrect number, especially when that number supplies monitoring. The tests cover trimming and deletion around a consumer group's last delivered position, so the correction concerns the history behind the metric as well as its displayed result.

The security fix determines the urgency of Valkey 8.1.1. Its implementation gives a precise answer to which connections receive the extra reply limit, while the supporting patches repair different connection and cluster transitions. For an existing service, these are changes to resource control and state publication beneath the commands its applications already issue.