Skip to content

fix(capi): reject null and atom objects in raw vector readers - #21

Merged
protocolstardust merged 2 commits into
RayforceDB:masterfrom
belowzeroff:fix/capi-raw-reader-guards
Jul 24, 2026
Merged

fix(capi): reject null and atom objects in raw vector readers#21
protocolstardust merged 2 commits into
RayforceDB:masterfrom
belowzeroff:fix/capi-raw-reader-guards

Conversation

@belowzeroff

Copy link
Copy Markdown
Contributor

Problem

Two C readers dereference the wrapped ray_t* without any validation, so they can crash the interpreter from pure Python:

>>> from rayforce import _rayforce_c as r
>>> r.read_vector_raw(r.RayObject())   # obj == NULL
Segmentation fault (exit 139)

read_u8_vector has the same unchecked obj->obj->type access. A second, subtler case: passing an atom to read_vector_raw passes the ray_scalar_elem_size check (it takes abs(type)), but atoms alias len with their payload, so the reader fabricates a garbage byte length for PyBytes_FromStringAndSize — an out-of-bounds read of arbitrary size.

RayObject is constructible from Python (tp_new = PyType_GenericNew, and the .pyi stub declares __init__), so nothing prevents these calls; every other reader in the binding already guards via check_type / unwrap_vec.

Fix

Bring the two stragglers in line with the rest of the binding:

  • read_vector_raw: reject NULL and non-vector objects (ray_is_vec) with RuntimeError before touching type/len.
  • read_u8_vector: fold the NULL check into the existing type check.

No behavior change for valid inputs — all existing callers (Vector.to_list/to_numpy/__getitem__, F32.to_python, IPC/WS send paths) pass real vectors.

Tests

test_read_vector_raw_rejects_non_vectors — bare RayObject() and an i64 atom now raise RuntimeError for both readers, and a real vector still round-trips (3 * 8 bytes). The test segfaults the interpreter on master.

Full suite: 2328 passed, 7 skipped (macOS arm64, core 2.5.0, extension rebuilt from this branch).

belljack and others added 2 commits July 19, 2026 10:26
read_vector_raw / read_u8_vector dereferenced the wrapped pointer
without checks: a bare RayObject() (obj == NULL) segfaulted the
interpreter, and an atom passed to read_vector_raw read a garbage
length because atoms alias len with their payload. Both now raise
RuntimeError like the other readers.
@protocolstardust
protocolstardust merged commit 15cea32 into RayforceDB:master Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants