Skip to content

fix(ffi): reject a null stream callback at registration#825

Merged
userFRM merged 1 commit into
mainfrom
fix/cabi-null-callback-guard
Jun 16, 2026
Merged

fix(ffi): reject a null stream callback at registration#825
userFRM merged 1 commit into
mainfrom
fix/cabi-null-callback-guard

Conversation

@userFRM

@userFRM userFRM commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Why

thetadatadx_client_set_callback and thetadatadx_streaming_set_callback typed the callback as a bare extern "C" fn, which Rust treats as non-nullable. A C / Go consumer can still pass NULL (the C ABI permits 0). The null was stored and then invoked on the dispatcher thread when the first event arrived — a call through address 0 that faults the process and cannot be contained by the catch_unwind boundary. It passes a register-and-return smoke test and crashes in production on the first event. This is the only caller pointer that is invoked rather than read, and was the lone pointer arg with no null guard.

Fix

Model the parameter as Option<ThetaDataDxStreamCallback> and reject None at registration with a normal -1 + thetadatadx_last_error() message. Option<extern "C" fn> uses the null niche, so the C ABI representation, the public header, and the exported symbol are byte-identical — no consumer change. (The compiler rejects a raw as *const ()).is_null() check as useless-ptr-null-checks precisely because the bare fn type is assumed non-null; Option is the idiomatic, lint-clean expression.)

Test

cargo test -p thetadatadx-ffi --lib null_callback — pins the None-niche contract the guards depend on. C-ABI completeness check clean (325 symbols, bidirectional parity); cargo clippy -p thetadatadx-ffi --all-targets -- -D warnings clean.

🤖 Generated with Claude Code

A C caller can pass a null function pointer to the set-callback
entries. The extern "C" fn type is non-nullable in Rust's model, so the
null was stored and later invoked on the dispatcher thread when the
first event arrived, dereferencing address 0 as a process-level fault
the unwind boundary cannot contain. Model the parameter as
Option<ThetaDataDxStreamCallback> so the null bit pattern is
representable and reject it up front with a recoverable error code. The
C ABI repr is unchanged (the null niche), so the header and exported
symbol are identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@userFRM userFRM enabled auto-merge (squash) June 16, 2026 14:54
@userFRM userFRM merged commit d4f97a8 into main Jun 16, 2026
43 checks passed
@userFRM userFRM deleted the fix/cabi-null-callback-guard branch June 16, 2026 15:29
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.

2 participants