Repository navigation
[Rust] Generate struct accessors without raw pointer operations (fixes enum arrays on big-endian) - #9472
Open
akhmittra wants to merge 1 commit into
Open
[Rust] Generate struct accessors without raw pointer operations (fixes enum arrays on big-endian)#9472akhmittra wants to merge 1 commit into
akhmittra wants to merge 1 commit into
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Struct accessors generated by `flatc --rust` used ptr::copy_nonoverlapping,
MaybeUninit::assume_init, ptr::copy with pointer arithmetic, slice::from_raw_parts
and raw pointer casts. They were in bounds only because the generator computes
offsets correctly; nothing at the Rust level enforced it.
Generate them as bounds-checked slices of the struct's [u8; N] storage instead:
scalars use {from,to}_ne_bytes on the type's EndianScalar::Scalar, array setters
convert element by element, Push copies self.0, and nested struct getters use the
bounds-checked follow_cast_ref. A wrong size is now a type error and a wrong offset
a panic, never an out-of-bounds access. No runtime API change.
This also fixes arrays of enums wider than one byte on big-endian hosts: they were
copied in host byte order instead of little-endian, so the wire bytes were swapped
and values read back through the Object API were wrong.
Adds tests/rust_struct_accessors_test.fbs and struct_accessors_test.rs, which check
exact wire bytes (so they catch endianness bugs on the MIPS CI job) and guard
against raw pointer operations reappearing in generated struct code.
akhmittra
force-pushed
the
rust-safe-struct-accessors
branch
from
November 9, 2026 11:04
cce0cd7 to
4cbe943
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
flatc --rustgenerated struct accessors using raw pointer operations(
ptr::copy_nonoverlapping+MaybeUninit::assume_init,ptr::copywith.add(),slice::from_raw_parts, and&*(ptr as *const T)casts). This code is compiled intoevery downstream crate that uses a schema with structs. It was in bounds only because
the C++ generator computes offsets and sizes correctly; nothing at the Rust level checked it.
This PR generates struct accessors as bounds-checked slices of the struct's
[u8; N]storage:copy_nonoverlapping+assume_init[u8; SIZE]+Scalar::from_ne_bytescopy_nonoverlappingself.0[OFF..OFF+SIZE].copy_from_slice(&x_le.to_ne_bytes())emplace_scalar_arraychunks_exact_mut+ per-elementto_little_endianptr::copy(.., self.0.as_mut_ptr().add(off), size)chunks_exact_mut+copy_from_slice&*(self.0[off..].as_ptr() as *const T)follow_cast_ref(bounds-checked)Pushslice::from_raw_parts(self as *const _ ..)dst.copy_from_slice(&self.0)A wrong size is now a type error and a wrong offset a panic, never an out-of-bounds access.
There is no runtime API change. The generated code uses only
coreAPIs available on MSRV 1.51,so it also works with already-published runtime versions. It complements #9304 (runtime scalar
access) without overlapping it.
Bug fix: enum arrays on big-endian
Arrays of enums went through the raw
ptr::copypath, writing the enum's host-endianrepresentation while the getter decodes little-endian. On big-endian targets,
[E:N]withenum E : short(or any enum wider than one byte) was written byte-swapped, and values read backwere wrong. Existing schemas only use one-byte enums in arrays, so CI didn't catch it.
With the new test schema on s390x (qemu-user), code from the current generator writes
[1, 2, 255, 254, 0, 1]instead of[2, 1, 254, 255, 1, 0]for[WideEnum::B, C, A], and theObject API round trip returns
[<UNKNOWN 513>, <UNKNOWN -257>, <UNKNOWN 256>]instead of[B, C, A]. With this PR all 7 new tests pass on s390x.Tests
tests/rust_struct_accessors_test.fbscoversshortandulongenum arrays, struct arrays,bool arrays, nested structs, f32/f64 and padding.
tests/rust_usage_test/tests/struct_accessors_test.rs(7 tests) checks the exactlittle-endian wire bytes against a hand-written layout, that each setter writes only its own
bytes, round trips through the builder, verifier and Object API, and includes a guard against
raw pointer ops reappearing in generated struct code.
rust_usage_test323/323 pass with and without--no-default-features. Alloc checks,serde test and
cargo clippyare clean.-Zbuild-std): fullrust_usage_test322/322 pass(
verify_struct_array_alignmentis little-endian only). On current master the new tests fail5/7, as described above.
rust_no_std_compilation_testbuilds, and the generated struct modulescompile for it.
cargo bench --bench benchmarks):traverse_canonical_buffer58 → 54 ns/iter;builder benchmarks unchanged within noise.
All
*_generated.rschanges come fromscripts/generate_code.pywith the patchedflatc(regenerating produces no further diff; regenerating with the unpatched
flatcreproduces master).🤖 Generated with Claude Code