Skip to content

FT.SEARCH/FT.AGGREGATE query with empty OR right-hand side ('@n:[0 10]|' or 'hello|the') crashes server (SIGSEGV) #1105

@madolson

Description

@madolson

Description

An FT.SEARCH (or FT.AGGREGATE) query whose OR operator | has an empty right-hand side crashes the server with a SIGSEGV (null-pointer dereference) in the filter parser. No indexed data is required — the crash happens at query-parse time. Reachable by any client that can run FT.SEARCH (a @read command).

Steps to reproduce

FT.CREATE idx ON HASH PREFIX 1 doc: SCHEMA n NUMERIC t TEXT
FT.SEARCH idx '@n:[0 10]|'      # trailing '|', empty OR right-hand side
FT.SEARCH idx 'hello|the'       # OR where the right operand is a stopword ('the')

Each drops the connection (Error: Server closed the connection). docker inspect ExitCode 139.

The general shape is any valid predicate immediately followed by an unescaped | whose right-hand side resolves to nothing: end-of-input, only whitespace, a closing ), or a stop word (the tokenizer drops stop words, yielding a null token). '@n:[0 10]||' does NOT crash — the inner recursion hits the second | with a null left operand and returns a graceful "Missing OR term" error, so the crash specifically requires the RHS to terminate empty.

Crash output

valkey ... crashed by signal: 11, si_code: 1
libsearch.so(_ZN13valkey_search12FilterParser13WrapPredicate...+0x286)
  at src/commands/filter_parser.cc:536
  <- FilterParser::ParseExpression
  <- FilterParser::Parse
  <- query::ParsePreFilter at src/query/search.cc

Root cause

src/commands/filter_parser.cc, ParseExpression, the '|' branch: it parses the right operand into predicate, then guards only the LEFT operand (if (result.prev_predicate) ... else return "Missing OR term"). It never checks that the right operand is non-null. A trailing | (RHS recursion returns null) or a stop-word RHS (ParseUnquotedTextToken returns a null token) passes a null new_predicate into WrapPredicate. There (around filter_parser.cc:487), the OR-flatten path dereferences new_predicate->GetType() on the null pointer, and a sibling path builds a ComposedPredicate owning a null child that crashes on destruction.

Suggested fix

After obtaining the RHS in the | branch, return InvalidArgumentError("Missing OR term") if it is null (mirror the existing left-operand guard), and/or null-check new_predicate in WrapPredicate.

Environment

  • valkey-search origin/main (8c260db); also reproduces on valkey/valkey-bundle:unstable
  • Confirmed live on current main: filter_parser.cc is untouched by the recent expr.cc crash fixes.

This issue was generated by AI but verified, with love, by a human.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions