Repository navigation
v4.3.6
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, and an over-long key hanging requests with the binary protocol (GHSA-p6pm-ch9v-44vx); unbounded retries (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). Also fixes set_multi with the binary protocol. 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 meta protocol needs it). A key under 250 characters but over 250 bytes, for example one with many non-ASCII characters, passed validation:
- with the meta protocol, memcached rejected it with the error reply above
- with the binary protocol, memcached dropped the connection, and
getorget_multiwith that key retried forever
- Such keys are now truncated like other long keys; keys that worked before are unchanged
- The reply parser affects
protocol: :metasince 3.2.0; the key length affects both protocols; 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 (
- Stop retrying an unresponsive server forever (GHSA-4qp6-2jcr-596v)
- 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
- 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
- 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 (a meta
VAsize or a binary body length) 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 raiseDalli::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)- With the binary protocol,
get,gatandfetch, and with the meta protocol,get_with_metadata, ignored 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 affects binarygetandgatin all versions, andget_with_metadatasince 4.2.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
- With the binary protocol,
- 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 - Fix
set_multiwith the binary protocol- Each quiet
setqwaited for a reply memcached never sends for a successful write, soset_multitimed out on its first key without storing anything (and, with retries, hung or marked the server down). Affects 4.2.0 and later
- Each quiet
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 (#1188)- Affects Ruby 3.2.x, 3.3.0–3.3.7 and 3.4.0–3.4.2. Fixed in 3.3.8 and 3.4.3; Ruby 3.2 reached end of life without the fix
- Ruby 3.1 doesn't use
IO#timeoutand isn't affected
Development:
- Drop Ruby 3.2 from the CI matrix, since that bug intermittently crashed its test runs (#1188)
- Fix three flaky tests: the
KeyManagernamespace key-length test, the Rack session freshness test, andMemcachedManager's start and stop handling (backport of #1141, #1182) - Fix flaky failover tests: move their ports out of Linux's ephemeral port range, and wait for memcached to accept connections after starting it (backport of #1184)