Repository navigation
v5.2.1
Security release. Fixes five vulnerabilities, released together in 5.2.1, 5.1.3, 5.0.9, 4.3.6 and 3.2.12: a pipelined get_multi returning another key's value (GHSA-p6pm-ch9v-44vx); unbounded retries and routing tokens that add meta flags (GHSA-4qp6-2jcr-596v); unbounded decompression and reply sizes (GHSA-3553-vcg5-72jw); per-request raw: true ignored on reads (GHSA-wr87-m4jw-29x5); and a forked child resending the parent's requests or ending its TLS session (GHSA-w39f-xq2m-4g8x). Upgrading is strongly recommended.
Security:
- Fix pipelined
get_multireturning one key's value for another after an error reply (GHSA-p6pm-ch9v-44vx)- The pipelined reply parser took any reply without a body for the end of the batch. An error reply for one key (
CLIENT_ERROR,SERVER_ERROR, orENfrom a proxy) ended it early, and the replies still on the connection were read as the replies to later commands, so agetcould return another key's value - Keys are now measured in bytes as sent, after base64 encoding when the key needs it. A key under 250 characters but over 250 bytes on the wire (for example, one with many non-ASCII characters) was the easiest way to cause that error reply. Such keys are now truncated like other long keys; keys that worked before are unchanged
- Affects the meta protocol since 3.2.0 (the default since 5.0.0); fixed in 5.2.1, 5.1.3, 5.0.9, 4.3.6 and 3.2.12
- The pipelined reply parser took any reply without a body for the end of the batch. An error reply for one key (
- Reject routing tokens that add meta flags, and stop retrying an unresponsive server forever (GHSA-4qp6-2jcr-596v)
p_tokenandl_tokennow reject whitespace and control characters, not just CR, LF and NUL. A space let a token add meta flags to the request it was sent with: changing an item's TTL on a read, creating stub items on a miss, turning a delete into a stale tombstone, or reading a different key- Affects 5.1.0 and later; fixed in 5.2.1 and 5.1.3
- A server that accepted connections but never answered (or dropped the connection on a request) was retried forever, hanging the caller, because each successful reconnect reset the failure count. Requests now fail after
socket_max_failuresattempts and the server is marked down, as when it can't be reached at all; it gets a full set of attempts again afterdown_retry_delay - Affects all versions; fixed in 5.2.1, 5.1.3, 5.0.9, 4.3.6 and 3.2.12
- Limit decompressed and reply value sizes (GHSA-3553-vcg5-72jw)
- Values flagged as compressed were inflated without limit, so a small stored value could expand to gigabytes on read, whatever the client's
compressorserializersettings. The newdecompressed_max_bytesoption (default 128 MiB;nildisables it) makes a read that would pass it raiseDalli::UnmarshalError. Custom compressors whosedecompresstakes only the data keep working, without the limit - The value size in a reply was used to read or buffer that many bytes, so a malicious server could make the client allocate gigabytes. Sizes over 1 GiB, memcached's largest item, now raise
Dalli::DalliErrorbefore reading - Affects all versions; fixed in 5.2.1, 5.1.3, 5.0.9, 4.3.6 and 3.2.12
- Values flagged as compressed were inflated without limit, so a small stored value could expand to gigabytes on read, whatever the client's
- Honor per-request
raw: trueon reads, and addDalli::JSONSerializer(GHSA-wr87-m4jw-29x5)get_multi,get_multi_cas,get_multi_with_metadata,get_casandget_with_metadataignored a per-requestraw: trueand deserialized the value according to its stored flags, so a caller who asked for raw bytes could still haveMarshal.loadrun on data someone else wrote to memcached. They now return the stored bytes- The
rawpart affectsget_with_metadatasince 4.2.0, and the other methods since they gained per-request options in 5.1.0 serializer: JSONreads values withJSON.load, which on json gem versions before 3.0 creates an object of the class named in a storedjson_classkey when the json additions are loaded. The newDalli::JSONSerializerreads withJSON.parseand only returns plain JSON types. The README and the Marshal security warning now recommend it;serializer: JSONitself is unchanged- The
serializer: JSONpart affects any version used with the json gem before 3.0
- Keep a forked child from writing to or closing the parent's connection (GHSA-w39f-xq2m-4g8x)
- A child forked while the parent had requests buffered but not yet sent, such as quiet writes inside a
quietblock, sent them again when it closed the client or reconnected: quiet writes ran twice, and the parent's later replies could be read as the replies to other requests, returning one key's value for another. Dalli now buffers requests itself and a forked child discards them - Affects 4.2.0 and later; fixed in 5.2.1, 5.1.3, 5.0.9 and 4.3.6
- With TLS, a forked child that closed the client (or reconnected after detecting the fork) sent a TLS close on the connection the parent was still using, ending the parent's session. A forked child now closes only its own copy of the socket
- Affects all versions with TLS; fixed in 5.2.1, 5.1.3, 5.0.9, 4.3.6 and 3.2.12
- A child forked while the parent had requests buffered but not yet sent, such as quiet writes inside a
Bug fixes:
- An exception raised by a
get_multiblock (such asTimeout::Error) was treated as a network failure: swallowed, with the wholeget_multiretried and keys yielded twice. It now reaches the caller
Notes:
- Document Ruby bug 21195, a VM crash (
[BUG] rb_sys_fail_path_in(io_fillbuf, ...) - errno == 0) when a socket read usingIO#timeoutis interrupted by a signal, in the README (#1189)- Affects Ruby 3.3.0–3.3.7 and 3.4.0–3.4.2; fixed in 3.3.8 and 3.4.3
Development:
- Add property tests for request building and fuzz tests for the reply parsers
- Add a security policy (
SECURITY.md), give every workflow a least-privilege token, and pin third-party actions by commit SHA (#1190) - Fix flaky failover tests (#1184)
- Their ports were inside Linux's ephemeral port range (32768-60999), so an earlier client connection could hold one as its local port. memcached then couldn't bind it on IPv4 and listened on IPv6 only, and the test's client marked that server down. They now use ports in the 26xxx range
- The test helper now waits until memcached accepts connections, instead of sleeping a fixed 0.1s after starting it
- Refresh apt's package index before installing libevent in CI, so a stale runner image can't break every memcached build (#1172)