Uh oh!
There was an error while loading. Please reload this page.
Fix transform_cypher_param to pass agtype_access_operator a VARIADIC array - #2489
Open
vividy163 wants to merge 2 commits into
Open
Fix transform_cypher_param to pass agtype_access_operator a VARIADIC array#2489vividy163 wants to merge 2 commits into
vividy163 wants to merge 2 commits into
Conversation
…array agtype_access_operator is declared VARIADIC agtype[] and every other call site in cypher_expr.c packs its arguments into a single agtype[] ArrayExpr via make_agtype_array_expr() and sets funcvariadic = true on the resulting FuncExpr. transform_cypher_param() was the sole exception: it passed the Param (the bound $1 agtype argument to cypher()) and the key Const directly as separate list elements to makeFuncExpr, never wrapped them in an ArrayExpr, and never set funcvariadic. With the Simple Query protocol PostgreSQL implicitly assembles the variadic array from the separate arguments at execution time, which masked the bug. Under the Extended Query protocol (prepared statements via Parse/Bind/Execute) the executor did not perform that implicit assembly, so agtype_access_operator read past its single agtype argument treating adjacent memory as additional array elements. The resulting out-of-bounds read surfaced as corrupted relation names in 'relation does not exist' error responses (random bytes mixed with fragments of in-flight query text / property values such as 'November', 'customer', 'communication', control bytes including 0x01), which client drivers then failed to UTF-8 decode: UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9e in position 51: invalid start byte The fix matches every other call site: wrap the two arguments in make_agtype_array_expr() and set func_expr->funcvariadic = true. A regression test is added that PREPAREs a CREATE EDGE statement whose MATCH clause resolves $src_id/$tgt_id scalar parameters (the exact shape used by LightRAG / asyncpg) and EXECUTEs it repeatedly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
agtype_access_operatoris declaredVARIADIC agtype[]. Every other call site incypher_expr.cpacks its arguments into a singleagtype[]ArrayExprviamake_agtype_array_expr()and setsfuncvariadic = trueon the resultingFuncExpr(seetransform_cypher_map_projection,transform_cypher_indirection).transform_cypher_param()— the path that turns a Cypher$paramreference into an AGE function call — was the only exception. It passed the bound Param and the key-name Const directly as separateFuncExprarguments without wrapping them and without settingfuncvariadic.With the Simple Query protocol PostgreSQL implicitly assembles the variadic array from separate arguments at execution time, which masked the bug. Under the Extended Query protocol (Parse/Bind/Execute prepared statements) the executor does not perform that implicit assembly, so
agtype_access_operatorread past its singleagtypeargument and treated adjacent memory as additional array elements. The resulting out-of-bounds read surfaced as corrupted relation names in "relation does not exist" error responses (random bytes mixed with fragments of in-flight query text / property values such as "November", "customer", "communication", and control bytes like0x01). Drivers that UTF-8 decode the server error then raised:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9e in position 51: invalid start byte
The bug reproduces with any Open/asyncpg/JDBC driver that uses prepared statements with bound
agtypeparameters against a Cypher query that references$param(for exampleMERGE (n:base {entity_id: $entity_id})orMATCH (s:base {id: $src_id}), (t:base {id: $tgt_id}) CREATE (s)-[r:DIRECTED {…}]->(t), the shape used by LightRAG). The error content is unrelated to the payload — edges with pure-ASCII property values failed identically.Fix
Pack the two arguments in
make_agtype_array_expr()and setfunc_expr->funcvariadic = true, exactly matching every other call site.Regression test
Added p_create_edge to regress/sql/cypher_create.sql, which PREPAREs a MATCH … {id: $src_id}, … {id: $tgt_id} CREATE ()-[:e {id: $edge_id}]->() statement (the exact shape used by LightRAG/asyncpg) and EXECUTEs it repeatedly.