Refine the ABI spec on Free* and the lifetime of diagnostic sink.

PiperOrigin-RevId: 1008908895
diff --git a/centipede/engine_abi.h b/centipede/engine_abi.h
index cdb1f84..d43c19f 100644
--- a/centipede/engine_abi.h
+++ b/centipede/engine_abi.h
@@ -205,9 +205,16 @@
                               FuzzTestInputHandle input);
 
   // Callback to run when the engine is done with `input`.
+  //
+  // The engine calls this at most once on each input emitted to the engine,
+  // even if any error has been emitted.
   void (*FreeInput)(FuzzTestAdapterCtx* ctx, FuzzTestInputHandle input);
 
   // Callback to run when the engine is done with `ctx` (and the adapter).
+  //
+  // The engine calls this at most once on each adapter after
+  // `ConstructAdapter`, even if any error has been emitted (including during
+  // `ConstructAdapter`).
   void (*FreeCtx)(FuzzTestAdapterCtx* ctx);
 } FuzzTestAdapter;
 
@@ -225,8 +232,11 @@
 
   // Constructs an adapter of the test into `adapter_out`. Any diagnostics
   // happening during the construction or running the adapter should be emitted
-  // to `diagnostic_sink`. `diagnostic_sink` is guaranteed to live until
-  // `FreeCtx` is called on the adapter.
+  // to `diagnostic_sink`.
+  //
+  // When this returns, `adapter_out->FreeCtx` must be set, even if any error
+  // has been emitted to `diagnostic_sink`. `diagnostic_sink` is guaranteed to
+  // stay alive until the `FreeCtx` call on the adapter has returned.
   void (*ConstructAdapter)(FuzzTestAdapterManagerCtx* ctx,
                            const FuzzTestDiagnosticSink* diagnostic_sink,
                            FuzzTestAdapter* adapter_out);
diff --git a/rust/engine/src/engine_ffi.rs b/rust/engine/src/engine_ffi.rs
index 276b3e1..6eb2542 100644
--- a/rust/engine/src/engine_ffi.rs
+++ b/rust/engine/src/engine_ffi.rs
@@ -192,12 +192,10 @@
 /// Constants for the layout of the coverage feature as a 64-bit unsigned
 /// integer:
 ///
-///   - Bits 63..59: 5-bit domain ID of the feature. Each domain is a
-///     logically independent feature namespace registered in
-///     `FuzzTestAdapter::SetUpCoverageDomains`.
+///   - Bits 63..59: 5-bit domain ID of the feature. Each domain is a logically independent feature
+///     namespace registered in `FuzzTestAdapter::SetUpCoverageDomains`.
 ///   - Bits 58..32: 27-bit feature ID within the domain.
 ///   - Bits 31..0:  32-bit counter value of the feature.
-///
 pub struct FuzzTestCoverageFeatureLayout;
 
 impl FuzzTestCoverageFeatureLayout {
@@ -377,10 +375,17 @@
     >,
 
     /// Callback to run when the engine is done with `input`.
+    ///
+    /// The engine calls this at most once on each input emitted to the engine,
+    /// even if any error has been emitted.
     pub free_input:
         Option<unsafe extern "C" fn(ctx: *mut FuzzTestAdapterCtx, input: FuzzTestInputHandle)>,
 
     /// Callback to run when the engine is done with `ctx` (and the adapter).
+    ///
+    /// The engine calls this at most once on each adapter after
+    /// `ConstructAdapter`, even if any error has been emitted (including during
+    /// `ConstructAdapter`).
     pub free_ctx: Option<unsafe extern "C" fn(ctx: *mut FuzzTestAdapterCtx)>,
 }
 
@@ -407,8 +412,11 @@
 
     /// Constructs an adapter of the test into `adapter_out`. Any diagnostics
     /// happening during the construction or running the adapter should be emitted
-    /// to `diagnostic_sink`. `diagnostic_sink` is guaranteed to live until
-    /// `FreeCtx` is called on the adapter.
+    /// to `diagnostic_sink`.
+    ///
+    /// When this returns, `adapter_out->FreeCtx` must be set, even if any error
+    /// has been emitted to `diagnostic_sink`. `diagnostic_sink` is guaranteed to
+    /// stay alive until the `FreeCtx` call on the adapter has returned.
     pub construct_adapter: Option<
         unsafe extern "C" fn(
             ctx: *mut FuzzTestAdapterManagerCtx,
diff --git a/rust/engine/src/lib.rs b/rust/engine/src/lib.rs
index f6e0bf8..7ee36e6 100644
--- a/rust/engine/src/lib.rs
+++ b/rust/engine/src/lib.rs
@@ -66,7 +66,7 @@
     /// * `raw` is a non-null, properly aligned pointer to a valid `FuzzTestDiagnosticSink`.
     /// * `(*raw).ctx` is a valid context pointer.
     /// * The sink and its context remain valid for the duration of `DiagnosticSink`'s usage
-    ///   (guaranteed by the engine until `FreeCtx` is called on the adapter).
+    ///   (guaranteed by the engine until the `FreeCtx` call on the adapter has returned).
     pub unsafe fn from_raw(raw: *const engine_ffi::FuzzTestDiagnosticSink) -> Self {
         // SAFETY: Caller guarantees `raw` is a valid pointer to `FuzzTestDiagnosticSink`.
         let raw_sink = unsafe { *raw };
@@ -124,8 +124,8 @@
     /// The caller must ensure that:
     /// * `raw` is a non-null, properly aligned pointer to a valid `FuzzTestBytesSink`.
     /// * `(*raw).ctx` is a valid context pointer.
-    /// * The sink and its context remain valid for the duration of `BytesSink`'s usage
-    ///   (guaranteed by the engine until `FreeCtx` is called on the adapter).
+    /// * The sink and its context remain valid for the duration of `BytesSink`'s usage (guaranteed
+    ///   by the engine until `FreeCtx` is called on the adapter).
     pub unsafe fn from_raw(raw: *const engine_ffi::FuzzTestBytesSink) -> Self {
         // SAFETY: Caller guarantees `raw` is a valid pointer to `FuzzTestBytesSink`.
         let raw_sink = unsafe { *raw };
@@ -158,8 +158,8 @@
     /// The caller must ensure that:
     /// * `raw` is a non-null, properly aligned pointer to a valid `FuzzTestInputSink`.
     /// * `(*raw).ctx` is a valid context pointer.
-    /// * The sink and its context remain valid for the duration of `InputSink`'s usage
-    ///   (guaranteed by the engine until `FreeCtx` is called on the adapter).
+    /// * The sink and its context remain valid for the duration of `InputSink`'s usage (guaranteed
+    ///   by the engine until `FreeCtx` is called on the adapter).
     pub unsafe fn from_raw(raw: *const engine_ffi::FuzzTestInputSink) -> Self {
         // SAFETY: Caller guarantees `raw` is a valid pointer to `FuzzTestInputSink`.
         let raw_sink = unsafe { *raw };
@@ -269,8 +269,8 @@
     /// The caller must ensure that:
     /// * `raw` is a non-null, properly aligned pointer to a valid `FuzzTestCoverageDomainRegistry`.
     /// * `(*raw).ctx` is a valid context pointer.
-    /// * The registry and its context remain valid for the duration of `CoverageDomainRegistry`'s usage
-    ///   (guaranteed by the engine until `FreeCtx` is called on the adapter).
+    /// * The registry and its context remain valid for the duration of `CoverageDomainRegistry`'s
+    ///   usage (guaranteed by the engine until `FreeCtx` is called on the adapter).
     pub unsafe fn from_raw(raw: *const engine_ffi::FuzzTestCoverageDomainRegistry) -> Self {
         // SAFETY: Caller guarantees `raw` is a valid pointer to `FuzzTestCoverageDomainRegistry`.
         let raw_registry = unsafe { *raw };
diff --git a/rust/src/worker.rs b/rust/src/worker.rs
index fcfbd6a..0ca12e9 100644
--- a/rust/src/worker.rs
+++ b/rust/src/worker.rs
@@ -343,7 +343,7 @@
 /// The caller must ensure that:
 /// * `ctx` is a valid pointer to the `RustFuzzTestAdapterManager` passed during initialization.
 /// * `diagnostic_sink` is a valid pointer to a `FuzzTestDiagnosticSink` whose lifetime extends
-///   until `FreeCtx` is called on the adapter.
+///   until the `FreeCtx` call on the adapter has returned.
 /// * `adapter_out` is a valid pointer to write the output `FuzzTestAdapter`.
 pub unsafe extern "C" fn construct_adapter_callback(
     ctx: *mut engine_ffi::FuzzTestAdapterManagerCtx,
@@ -355,7 +355,8 @@
     let manager = unsafe { &*(ctx as *const RustFuzzTestAdapterManager) };
 
     // SAFETY: The engine guarantees `diagnostic_sink` is a valid pointer to a
-    // `FuzzTestDiagnosticSink` whose lifetime extends until `FreeCtx` is called on the adapter.
+    // `FuzzTestDiagnosticSink` whose lifetime extends until the `FreeCtx` call on the adapter has
+    // returned.
     let safe_sink = unsafe { DiagnosticSink::from_raw(diagnostic_sink) };
 
     set_diagnostic_sink(safe_sink);