fix(ffi): regenerate hiroz_ffi.h to match the Rust FFI surface - #343
richardw347 wants to merge 1 commit into
Conversation
The committed C header had drifted from the #[repr(C)] types in crates/hiroz/src/ffi. Most importantly hiroz_context_config_t was missing its trailing `namespace` field: the Rust CContextConfig has it (read in hiroz_context_create_with_config), but the header stopped at `enable_logging`. A caller compiling against this header allocates a struct 8 bytes smaller than the Rust side expects, so the FFI reads `cfg.namespace` past the caller's allocation — an uninitialised pointer that is then passed to cstr_to_str(), i.e. UB / a likely segfault or a spurious context-build failure. The header had also fallen behind on several constants that exist in the FFI surface (KEEP_ALL_CACHE_DEPTH, the parameter-type enum values, DEPTH_RECURSIVE) plus updated doc comments. Regenerated with cbindgen 0.29.4 via the crate's own build.rs (cargo build --features ffi), so it now matches the compiled staticlib. No hand edits; purely the generator output. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Reviewed this against the current FFI surface. Looks clean — purely additive
The One pre-existing oddity, not introduced here: the regenerated doc comment on No concerns — this is a straightforward, correct regen. |
|
Following up on the review above: it turns out there was already an internal PR (#277) fixing this exact #277 additionally adds a CI check that regenerates the header at build time and fails if it drifts from what's committed, so it covers this defect and prevents it from recurring. It'll land instead of this PR, with credit to you in its description for independently catching the same bug through a real Go/cgo consumer — thanks for the report and the clean fix. Closing this in favor of #277. |
Summary
The committed C header
crates/hiroz-go/hiroz/hiroz_ffi.hhas drifted from the#[repr(C)]types incrates/hiroz/src/ffi. The header wasn't regenerated afterseveral FFI changes landed, so consumers compile against a stale ABI.
The consequential drift is in
hiroz_context_config_t. The RustCContextConfigends with anamespace: *const c_charfield, read inhiroz_context_create_with_config:…but the committed header stops at
enable_logging(11 fields):A caller that allocates this struct from the header gets one 8 bytes smaller
than the Rust side reads.
hiroz_context_create_with_configthen dereferencescfg.namespace— memory just past the caller's allocation, i.e. anuninitialised/garbage pointer — and passes it to
cstr_to_str(). That is UB;in practice it presents either as a segfault during context creation or as a
spurious
-16 "failed to build entity: context"when the garbage happens to bea non-null but invalid pointer.
The header had also fallen behind on constants that exist in the FFI surface
(
KEEP_ALL_CACHE_DEPTH, the parameter-type enum values,DEPTH_RECURSIVE) andon a couple of doc comments.
Fix
Regenerate the header from the crate's own
build.rscbindgen path(
cargo build --features ffi, cbindgen 0.29.4). No hand edits — the diff ispurely generator output, and every added item corresponds to a real exported
Rust symbol (verified:
KEEP_ALL_CACHE_DEPTHinqos,DEPTH_RECURSIVEinparameter/wire_types,namespace_inCContextConfig). Nothing is removed.How we hit this
We consume
libhiroz.afrom Go via cgo. Against a busyrmw_zenoh_cpprouter,hiroz_context_create_with_configintermittently returned-16/ segfaulted.The 8-byte struct mismatch was one of the contributing ABI hazards we traced
(and it is latent for any header-based consumer, not just ours).
Suggested follow-up (optional, not in this PR)
To stop the header re-drifting, consider a CI check that regenerates the header
and fails if it differs from the committed copy (
cbindgen ... --output - | diff),or make
build.rshard-error when theffifeature is set butcbindgenisabsent (it currently prints a warning and silently ships the stale header).
Verification
cargo build --release --features ffi,jazzy --no-default-features -p hirozregenerates the header cleanly; struct tail now ends
const char *namespace_;.