existential-birds/beagle/ffi-code-review
Claude Code and agent skill by existential-birds/beagle
- community
Checking that the SKILL.md file is still in the repository.
Reviews Rust FFI code for type safety, memory layout compatibility, string handling, callback patterns, and unsafe boundary correctness. Use when reviewing extern blocks, #[repr(C)] types, bindgen output, or code calling C/C++ libraries.
Install
npx vskill@latest install existential-birds/beagle/ffi-code-reviewSKILL.md
FFI Code Review
Review Workflow
- Check Cargo.toml -- Note Rust edition (2024 has breaking changes to extern blocks and unsafe attributes),
build-dependencies(bindgen, cc, pkg-config),crate-type(cdylib,staticlib), andlinkskey - Check build.rs -- Verify link directives (
cargo:rustc-link-lib,cargo:rustc-link-search), bindgen configuration, and C source compilation - Check extern blocks -- Verify calling conventions, symbol declarations, and safety annotations
- Check type layout -- Every type crossing FFI must be
#[repr(C)]or a primitive FFI type - Check string and pointer handling -- CStr/CString usage, null checks, ownership transfers
- Check callbacks --
extern "C" fnpointers, panic safety across FFI boundary - Gates -- Complete Gates below before reporting; do not skip ahead on “internal verification”
Gates
Complete in order. Do not emit findings until Gate 4 passes for each issue.
Gate 1 — Crate context (on disk) PASS when: You opened the reviewed crate’s Cargo.toml (workspace member path if applicable) and recorded edition =, plus any of links, crate-type, or build-dependencies that matter for this FFI. Blocks rationalization: Edition-specific findings (unsafe extern "C" {}, #[unsafe(no_mangle)], etc.) require this — if edition is not 2024, do not flag 2024-only requirements.
Gate 2 — Linkage and binding sources PASS when: If the crate links native code or uses bindgen/pkg-config, you opened build.rs (or the checked-in bindings entry point). If there is no build.rs, you stated that bindings are hand-written and reviewed those extern / include! sites. Artifact: At least one path you opened (e.g. build.rs, src/ffi.rs, or OUT_DIR bindings via include!).
Gate 3 — Code evidence PASS when: Every planned finding has a target [FILE:LINE] from a full function/block you read, not only diff hunks or partial snippets.
Gate 4 — Pre-report protocol PASS when: You loaded and applied the review-verification-protocol skill, including FFI-Specific Verification for repr(C), safety comments, ownership/callbacks, or bindgen-heavy code.
Output Format
Report findings as:
Quick Reference
| Issue Type | Reference | |
|---|---|---|
| C-to-Rust type mapping, repr(C) layout, enums, opaque types | references/type-mapping.md | |
| Safe wrappers, ownership transfer, callbacks, build.rs, testing | references/safety-patterns.md |
Review Checklist
extern Blocks and Calling Conventions
- [ ] Foreign function declarations use
extern "C"(explicit, not bareextern) - [ ] Edition 2024:
extern "C" {}blocks written asunsafe extern "C" {} - [ ] Functions exposed to C use
extern "C" fn(not default Rust calling convention) - [ ] Calling convention matches the foreign library (
"C","system"for Win32 API) - [ ]
#[link(name = "...")]specifies the correct library name - [ ]
#[link(name = "...", kind = "static")]used when statically linking
Symbol Management
- [ ] Exported functions use
#[no_mangle]to preserve symbol names - [ ] Edition 2024:
#[no_mangle]written as#[unsafe(no_mangle)] - [ ] Edition 2024:
#[export_name = "..."]written as#[unsafe(export_name = "...")] - [ ]
#[link_name = "..."]used when Rust name differs from C symbol - [ ] Exported items are
pub(only public#[no_mangle]symbols appear in library output)
Type Layout
- [ ] Every struct/union crossing FFI has
#[repr(C)]-- Rust's default layout is undefined - [ ] Primitive types use
std::ffi/std::os::rawequivalents (c_int,c_char,c_void) - [ ] No bare
i32where C usesint-- usec_int(width varies by platform) - [ ] Quirky C types like
__be32use byte arrays ([u8; 4]), not Rust integers - [ ] Enums crossing FFI use
#[repr(C)]or#[repr(u8)]/#[repr(u32)]with explicit discriminants - [ ] C-style bitflag enums use a newtype around an integer (or
bitflagscrate), not a Rust enum - [ ]
#[non_exhaustive]on enums representing C enumerations that may gain new values
String Handling
- [ ] C strings use
CStr(borrowed) orCString(owned), never&strorString - [ ]
CString::new()result is checked for interior null bytes (returnsErron\0) - [ ]
CStringoutlives any*const c_charpointer derived from it via.as_ptr() - [ ] Incoming
*const c_charvalidated withCStr::from_ptr()insideunsafe - [ ] No assumption that C strings are valid UTF-8 -- use
to_str()which returnsResult - [ ] OS paths use
OsStr/OsStringandCStr, not&str
Ownership and Allocation
- [ ] Clear ownership contract: who allocates, who frees
- [ ] Rust-allocated memory freed by Rust (
Box::from_raw), C-allocated freed by C - [ ]
Box::into_raw/Box::from_rawpaired correctly for heap transfers - [ ]
Vec::into_raw_partsused when passing arrays to C (pointer + length + capacity) - [ ] Destructor functions exposed for every opaque Rust type given to C
- [ ] No
Droprunning on C-allocated memory (and vice versa)
Callbacks
- [ ] Callback types are
extern "C" fn(...), not closures orfn(...) - [ ] Callbacks use
std::panic::catch_unwindto prevent panics from unwinding across FFI - [ ] Callback context passed as
*mut c_voidwith safe reconstruction at call site - [ ]
Option<extern "C" fn(...)>used for nullable function pointers (niche optimization)
Bindgen and Build Scripts
- [ ] Bindgen output reviewed for correctness (auto-generated types may need adjustment)
- [ ]
-syscrate pattern used for raw bindings, separate crate for safe wrappers - [ ]
build.rsusescargo:rustc-link-libandcargo:rustc-link-searchcorrectly - [ ]
linkskey inCargo.tomlprevents duplicate linking of the same native library - [ ] Platform-specific bindings generated per-build (not checked in for a single platform)
Safety Documentation
- [ ] Every
unsafeblock has a// SAFETY:comment explaining invariants - [ ] Every public FFI wrapper function documents safety requirements
- [ ] Edition 2024:
unsafe fnbodies use explicitunsafe {}blocks around unsafe ops
Severity Calibration
Critical (Block Merge)
- Missing
#[repr(C)]on types crossing FFI boundary (undefined memory layout) - Wrong string handling:
&str/StringwhereCStr/CStringrequired - Ownership confusion: freeing C-allocated memory with Rust's allocator (or vice versa)
- Panic unwinding across FFI boundary without
catch_unwind - Using Rust enum for C bitflags (invalid discriminant = undefined behavior)
- Passing closure where
extern "C" fnpointer required
Major (Should Fix)
- Missing safety documentation on
unsafeblocks or public FFI functions - No null pointer check on incoming
*const T/*mut Tbefore dereferencing CStringdropped before its pointer is used by C (dangling pointer)- Missing
#[link(name = "...")]causing link failures on some platforms - Edition 2024:
externblock not markedunsafe extern - Edition 2024:
#[no_mangle]not wrapped in#[unsafe(...)]
Minor (Consider Fixing)
- Using
i32instead ofc_intfor Cint(correct on most platforms but not portable) - Missing
#[non_exhaustive]on enums mapping to extensible C enumerations - Verbose manual bindings where bindgen would be more maintainable
- Checked-in bindings without platform guards
Informational
- Suggestions to split raw bindings into a
-syscrate - Suggestions to add opaque wrapper types for distinct
*mut c_voidpointers - Suggestions to use
Option<NonNull<T>>for nullable pointers
Valid Patterns (Do NOT Flag)
- `unsafe extern "C" {}` in edition 2024 -- correct form for foreign declarations
- `#[unsafe(no_mangle)]` in edition 2024 -- correct form for symbol export
- `Option<extern "C" fn(...)>` for nullable callbacks -- niche optimization guaranteed
- `Option<NonNull<T>>` for nullable pointers -- zero-cost nullable pointer pattern
- `*mut c_void` for opaque C types -- standard when internal layout is irrelevant
- Distinct empty structs wrapping `c_void` for type-safe opaque pointers -- prevents pointer confusion
- `CStr::from_bytes_with_nul_unchecked` with compile-time literal -- safe when literal is known null-terminated
- `extern "C-unwind"` for controlled unwinding -- valid per RFC 2945
- `include!(concat!(env!("OUT_DIR"), "/bindings.rs"))` in bindgen crates -- standard pattern
- `Box::into_raw` / `Box::from_raw` pairs for ownership transfer -- correct pattern when paired
Before Submitting Findings
Complete Gates 1-4 in order before reporting any issue; Gate 4 incorporates the review-verification-protocol skill.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingplugins/beagle-rust/skills/ffi-code-review/SKILL.md
Popularity
- Repository stars
- 78
- Repository forks
- 9
- 7-day trend score
- 22
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 21 May 2026, 5 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/existential-birds/beagle/ffi-code-review)