Description
A JSON.DEL using a multi-match path ($.*, $..*) against an object whose keys contain a tilde (~) crashes the server with a SIGSEGV (out-of-bounds read). Any authenticated client can trigger it with two commands and no special configuration.
Steps to reproduce
JSON.SET k '$' '{"~":1,"~~":2}'
JSON.DEL k '$.*'
The server dies; the client sees Error: Server closed the connection.
Also reproduces via recursive descent:
JSON.SET t '$' '{"o":{"~":1,"~a":2}}'
JSON.DEL t '$..*'
Minimal condition: an object with >=2 keys each containing ~, deleted via a path that selects more than one such member. With a single match the comparator is never invoked, so it does not crash.
Crash output
=== VALKEY BUG REPORT START ===
crashed by signal: 11, si_code: 1
Accessing address: 0xfffffffffffffffc
...
libjson.so pathCompare::operator()(...)
-> std::set<...,pathCompare>::insert
-> _Rb_tree::_M_get_insert_unique_pos
Root cause
escape_member_name_for_json_pointer() (src/json/selector.cc:2426) escapes / -> ~1 but not ~ -> ~0, violating RFC 6901. Keys containing ~ therefore produce malformed JSON pointers.
In Selector::deleteValues() (src/json/selector.cc:678-689) these paths are inserted into a jsn::set<jsn::string, pathCompare>. The comparator pathCompare::operator() (src/json/selector.cc:611) does:
JPointer ptr1 = JPointer(path1);
auto depth1 = ptr1.GetTokenCount(); // 0 for the invalid pointer
... tokenArray1[depth1 - 1] ... // tokenArray[(size_t)-1] -> OOB read
GetTokenCount() returns 0 for the invalid pointer, so tokenArray[depth - 1] reads at offset (size_t)-1, faulting at 0xfffffffffffffffc.
Suggested fix
Escape ~ -> ~0 (before / -> ~1) in escape_member_name_for_json_pointer(), and/or guard pathCompare against GetTokenCount() == 0 / ptr.HasError().
Environment
- valkey-json commit
a932a1c, valkey/valkey-bundle:unstable
- Reproduced on Linux x86_64
This issue was generated by AI but verified, with love, by a human.
Description
A
JSON.DELusing a multi-match path ($.*,$..*) against an object whose keys contain a tilde (~) crashes the server with a SIGSEGV (out-of-bounds read). Any authenticated client can trigger it with two commands and no special configuration.Steps to reproduce
The server dies; the client sees
Error: Server closed the connection.Also reproduces via recursive descent:
Minimal condition: an object with >=2 keys each containing
~, deleted via a path that selects more than one such member. With a single match the comparator is never invoked, so it does not crash.Crash output
Root cause
escape_member_name_for_json_pointer()(src/json/selector.cc:2426) escapes/->~1but not~->~0, violating RFC 6901. Keys containing~therefore produce malformed JSON pointers.In
Selector::deleteValues()(src/json/selector.cc:678-689) these paths are inserted into ajsn::set<jsn::string, pathCompare>. The comparatorpathCompare::operator()(src/json/selector.cc:611) does:GetTokenCount()returns 0 for the invalid pointer, sotokenArray[depth - 1]reads at offset(size_t)-1, faulting at0xfffffffffffffffc.Suggested fix
Escape
~->~0(before/->~1) inescape_member_name_for_json_pointer(), and/or guardpathCompareagainstGetTokenCount() == 0/ptr.HasError().Environment
a932a1c,valkey/valkey-bundle:unstableThis issue was generated by AI but verified, with love, by a human.