AGE Version: apache/age master @ cfd3b63 (2026-08-14), extension 1.8.0, on PostgreSQL 18.6
Installation Method: Docker
API: SQL (psql)
Steps to reproduce
- On a fresh database, run (no data required):
LOAD 'age';
SET search_path = ag_catalog, public;
SELECT ag_catalog.age_tobooleanlist('[true]');
All four age_to*list functions crash deterministically on array-shaped literal arguments (12 shapes verified):
| Statement |
Result |
age_tobooleanlist('[true]'), ('[null]'), ('[true, null, false]') |
SIGSEGV |
age_tofloatlist('[true]'), ('[1]'), ('[null]') |
SIGSEGV |
age_tointegerlist('[true]'), ('[null]') |
SIGSEGV |
age_tostringlist('[true]'), ('[null]'), ('[1, null, "a"]') |
SIGSEGV |
Other array shapes ('[1]', '["a"]', '[1.5]' for the mismatched functions) give a bogus ERROR: toBooleanList() argument must resolve to a list or null instead — same root cause, non-crashing bit pattern.
Expected behavior
The Cypher equivalents work correctly and define the expected behavior:
SELECT * FROM cypher('g', $$ RETURN toBooleanList([true]) $$) AS (r agtype);
-- r = [true]
The SQL form should either return the same result or raise a clean error; it must never crash the backend.
Actual behavior
psql: server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
psql: error: connection to server was lost
Server log (docker logs):
LOG: client backend (PID 14030) was terminated by signal 11: Segmentation fault
LOG: all server processes terminated; reinitializing
Root cause (in src/backend/utils/adt/agtype.c): type confusion at the SQL boundary. For VARIADIC "any" functions, PostgreSQL resolves an unknown-type literal to text (verified with the same-signature builtin json_build_array('[true]') → ["[true]"]), i.e. the function receives a text[] (ArrayType) datum, not agtype. The C functions read the argument with AG_GET_ARG_AGTYPE_P(0) without ever checking get_fn_expr_argtype(), so the text[] datum is reinterpreted as an agtype container. Depending on the literal's byte pattern the garbage either passes the AGT_ROOT_IS_ARRAY root check and crashes while iterating elements (the 12 shapes above), or fails it and produces the bogus argument must resolve to a list or null error ('[1]', '["a"]', '[1.5]'). Passing an explicit ::agtype cast works correctly — age_tobooleanlist('[true]'::agtype) returns [true] — and the explicit VARIADIC ARRAY[...] form hits the same reinterpretation. EXPLAIN (VERBOSE) of a crashing call also crashes the backend.
Cypher is safe: literal lists are built through agtype_build_list (null elements get real value slots) and the transform passes a typed agtype expression, so the C code never sees a text[]; the PREPARE/EXECUTE parameter path was verified safe as well. Trigger surface = AGE's public SQL API (shared-instance DoS, same class as the ? crash).
AGE Version: apache/age master @ cfd3b63 (2026-08-14), extension 1.8.0, on PostgreSQL 18.6
Installation Method: Docker
API: SQL (psql)
Steps to reproduce
All four
age_to*listfunctions crash deterministically on array-shaped literal arguments (12 shapes verified):age_tobooleanlist('[true]'),('[null]'),('[true, null, false]')age_tofloatlist('[true]'),('[1]'),('[null]')age_tointegerlist('[true]'),('[null]')age_tostringlist('[true]'),('[null]'),('[1, null, "a"]')Other array shapes (
'[1]','["a"]','[1.5]'for the mismatched functions) give a bogusERROR: toBooleanList() argument must resolve to a list or nullinstead — same root cause, non-crashing bit pattern.Expected behavior
The Cypher equivalents work correctly and define the expected behavior:
The SQL form should either return the same result or raise a clean error; it must never crash the backend.
Actual behavior
Server log (docker logs):
Root cause (in
src/backend/utils/adt/agtype.c): type confusion at the SQL boundary. ForVARIADIC "any"functions, PostgreSQL resolves an unknown-type literal totext(verified with the same-signature builtinjson_build_array('[true]')→["[true]"]), i.e. the function receives atext[](ArrayType) datum, not agtype. The C functions read the argument withAG_GET_ARG_AGTYPE_P(0)without ever checkingget_fn_expr_argtype(), so thetext[]datum is reinterpreted as an agtype container. Depending on the literal's byte pattern the garbage either passes theAGT_ROOT_IS_ARRAYroot check and crashes while iterating elements (the 12 shapes above), or fails it and produces the bogusargument must resolve to a list or nullerror ('[1]','["a"]','[1.5]'). Passing an explicit::agtypecast works correctly —age_tobooleanlist('[true]'::agtype)returns[true]— and the explicitVARIADIC ARRAY[...]form hits the same reinterpretation.EXPLAIN (VERBOSE)of a crashing call also crashes the backend.Cypher is safe: literal lists are built through
agtype_build_list(null elements get real value slots) and the transform passes a typed agtype expression, so the C code never sees a text[]; the PREPARE/EXECUTE parameter path was verified safe as well. Trigger surface = AGE's public SQL API (shared-instance DoS, same class as the?crash).