You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Broaden the fix to all control characters, not just NUL
protocol.txt's actual rule is "must not include control characters or
whitespace" -- \s alone missed NUL plus the rest of C0 (0x01-0x08,
0x0E-0x1F) and DEL (0x7F). Switches to \p{Cntrl} (equivalent to POSIX
[:cntrl:], but without the "duplicated range" warning Ruby raises when
\s and [:cntrl:] are combined in one class -- fatal under this suite's
-w run).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
-`KeyRegularizer.required?` decided whether a key needed base64 encoding using `/\s/`, which does not match NUL -- a key that was otherwise ASCII-only and contained no whitespace (e.g. `"foo\x00bar"`) went out on the wire unencoded
43
-
- Not a command-injection risk: the text protocol splits commands on CRLF, not NUL. The risk is key confusion -- anything downstream that treats the key as a C string (memcached itself, a proxy, logging) could silently truncate at the NUL and act on a different, shorter key than Dalli believes it sent
44
-
- A raw NUL byte in a key was never protocol-compliant in the first place: memcached's own spec (`protocol.txt`) states a key "must not include control characters or whitespace," and NUL is a control character. The only sanctioned way to carry binary content in a key is the meta protocol's base64 (`b` flag) path -- the one whitespace and non-ASCII keys already used, and the one NUL-containing keys now use too
45
-
-**Behavior change:** a key containing a NUL byte now produces different bytes on the wire (base64-encoded, per the meta protocol's `b` flag) than before. Existing cache entries stored under the old, unencoded form of such a key will read as a miss once every reader has upgraded. **During a rolling deploy, old and new Dalli versions disagree about which physical key a NUL-containing logical key maps to** -- not just a one-time cutover, but ongoing inconsistency between the old-version and new-version server pools for the duration of the rollout. Harmless for an ordinary cached value (worst case, extra cache misses); worth accounting for if such a key ever backs something stateful, like a lock or counter. Expected to be rare in practice: embedding a raw NUL byte in a cache key is unusual, and doing so was already outside what the protocol permits
41
+
- Base64-encode keys containing control characters, not just NUL (#1148)
42
+
-`KeyRegularizer.required?` decided whether a key needed base64 encoding using `/\s/`, which matches most whitespace but none of the C0 control range (0x00-0x1F) or DEL (0x7F) -- a key that was otherwise ASCII-only and contained no whitespace (e.g. `"foo\x00bar"` or a key with an embedded ESC byte) went out on the wire unencoded
43
+
- Not a command-injection risk: the text protocol splits commands on CRLF, not other control bytes. The risk is key confusion -- anything downstream that treats one of these bytes specially (a C string terminating at NUL, a terminal or log line interpreting an escape byte) could silently act on a different key than Dalli believes it sent
44
+
- A raw control byte in a key was never protocol-compliant in the first place: memcached's own spec (`protocol.txt`) states a key "must not include control characters or whitespace." The only sanctioned way to carry such content in a key is the meta protocol's base64 (`b` flag) path -- the one whitespace and non-ASCII keys already used, and the one these keys now use too. The check is now `/[\s[:cntrl:]]/`, matching that rule directly rather than special-casing NUL
45
+
-**Behavior change:** a key containing a control character now produces different bytes on the wire (base64-encoded, per the meta protocol's `b` flag) than before. Existing cache entries stored under the old, unencoded form of such a key will read as a miss once every reader has upgraded. **During a rolling deploy, old and new Dalli versions disagree about which physical key such a logical key maps to** -- not just a one-time cutover, but ongoing inconsistency between the old-version and new-version server pools for the duration of the rollout. Harmless for an ordinary cached value (worst case, extra cache misses); worth accounting for if such a key ever backs something stateful, like a lock or counter. Expected to be rare in practice: embedding a raw control byte in a cache key is unusual, and doing so was already outside what the protocol permits
46
46
- Found while auditing `request_formatter.rb` during the routing-token work in #1130 / #1147; unrelated to that change and predates it
47
47
48
48
- Retry transient network errors in `get_multi`, `set_multi` and `delete_multi` instead of silently swallowing them (#1149)
0 commit comments