Summary
typecast_LONGINTEGER_cast (psycopg/typecast_basic.c) copies an integer-array element into a fixed 24-byte stack buffer with an unbounded strncpy. Any array element token longer than 24 bytes overflows the buffer and crashes the client process (SIGSEGV; reliably reproducible with tokens >= 64 bytes). typecast_INTEGER_cast / typecast_ROWID_cast are the same function.
A normal PostgreSQL server cannot produce such tokens (int8 maxes out at 19 digits), so this does not affect normal usage — it is a robustness gap: unexpected/malformed data reaching the array tokenizer crashes instead of failing cleanly.
Root cause
char buffer[24];
...
if (s[len] != '\0') { /* array tokens are not NUL-terminated */
strncpy(buffer, s, (size_t) len); /* len is not bounded */
buffer[len] = '\0';
s = buffer;
}
Array elements are sliced by typecast_array_tokenize() as substrings of the array literal (terminated by , / }), so s[len] != '\0' is always true and the copy branch runs with len unvalidated.
Repro
import psycopg2, psycopg2.extensions as e
conn = psycopg2.connect("dbname=test")
cur = conn.cursor()
e.LONGINTEGERARRAY(b"{" + b"9" * 1024 + b",1}", cur) # segfault (exit 139)
Suggested fix
Bound the copy (or use a dynamic buffer as in typecast_DECIMAL_cast):
if (s[len] != '\0') {
if (len >= (Py_ssize_t) sizeof(buffer)) {
/* avoid stack overflow on oversized tokens */
PyErr_SetString(PyExc_ValueError, "integer array element too long");
return NULL;
}
strncpy(buffer, s, (size_t) len);
buffer[len] = '\0';
s = buffer;
}
Happy to open a PR if this looks right.
Summary
typecast_LONGINTEGER_cast(psycopg/typecast_basic.c) copies an integer-array element into a fixed 24-byte stack buffer with an unboundedstrncpy. Any array element token longer than 24 bytes overflows the buffer and crashes the client process (SIGSEGV; reliably reproducible with tokens >= 64 bytes).typecast_INTEGER_cast/typecast_ROWID_castare the same function.A normal PostgreSQL server cannot produce such tokens (int8 maxes out at 19 digits), so this does not affect normal usage — it is a robustness gap: unexpected/malformed data reaching the array tokenizer crashes instead of failing cleanly.
Root cause
Array elements are sliced by
typecast_array_tokenize()as substrings of the array literal (terminated by,/}), sos[len] != '\0'is always true and the copy branch runs withlenunvalidated.Repro
Suggested fix
Bound the copy (or use a dynamic buffer as in
typecast_DECIMAL_cast):Happy to open a PR if this looks right.