vividy163 opened a new pull request, #2488:
URL: https://github.com/apache/age/pull/2488

   ## Root cause
   
   `agtype_access_operator` is declared `VARIADIC agtype[]`. 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` (see `transform_cypher_map_projection`, 
`transform_cypher_indirection`).
   
   `transform_cypher_param()` — the path that turns a Cypher `$param` reference 
into an AGE function call — was the only exception. It passed the bound Param 
and the key-name Const directly as separate `FuncExpr` arguments without 
wrapping them and without setting `funcvariadic`.
   
   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_operator` 
read past its single `agtype` argument 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 like `0x01`). 
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 `agtype` parameters against a Cypher query that 
references `$param` (for example `MERGE (n:base {entity_id: $entity_id})` or 
`MATCH (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 set 
`func_expr->funcvariadic = true`, exactly matching every other call site.
   
   ```c
   newa = make_agtype_array_expr(args);
   
   func_expr = makeFuncExpr(func_access_oid, AGTYPEOID, list_make1(newa),
                            InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
   func_expr->funcvariadic = true;
   
   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. The 
expected output file (regress/expected/cypher_create.out) needs to be 
regenerated by running make installcheck against a built extension.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to