This document captures the safety model for jvmti-bindings and a checklist to review before shipping an agent.
- Treat all JNI/JVMTI callbacks as
unsafeboundaries. - Never panic across a JNI/JVMTI callback boundary.
- Do not store or share
JNIEnvacross threads. - For embedded JVMs, only use
creator_envon the creating thread; attach/detach other threads. - Use
GlobalReffor long-lived references and ensure they are released. - Always check JVMTI error codes and handle failures explicitly.
- Assume callbacks can be concurrent and re-entrant.
- Avoid long-running work inside callbacks; offload to worker threads.
- Respect callback-specific constraints (some callbacks forbid JNI).
- Capabilities requested in
on_loadmatch the events you enable. - Event callbacks are registered before enabling notifications.
JNIEnvis only used on the thread that provided it.- No
unwrap()or panics in callback code paths. - All JVMTI-allocated memory is deallocated via
Jvmti::deallocate. - Global and local references are cleaned up.
- Agent state is thread-safe (
Mutex, atomics, or lock-free). - You avoid JNI calls during
GarbageCollectionStart/Finishcallbacks. - Native method redirects or bytecode rewriting are validated and bounded.
- Any attach-based initialization is idempotent.
- Never assume null termination or UTF-8 validity for JVM-provided strings.
- Treat pointer lifetimes as scoped to the callback unless documented otherwise.
- Validate lengths before copying into Rust buffers.
- Prefer owned Rust structures over returning raw JVMTI structs.