1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Generic wasm-memory `(ptr, len)` bounds-check helpers shared by hook
//! and observer hosts.
//!
//! Both `read_caller_memory<T>` and `write_caller_memory<T>` enforce the
//! cryptographer-pinned bounds-check rule order before touching wasm
//! linear memory:
//!
//! 1. negative `len` → trap
//! 2. negative `ptr` → trap (BEFORE `len == 0` short-circuit;
//! defense-in-depth ordering)
//! 3. `len == 0` (or empty `bytes`) → no-op (memory export not required)
//! 4. missing `memory` export → trap
//! 5. `ptr + len` `u64` overflow → trap
//! 6. `ptr + len > memory.data_size()` → OOB trap
//!
//! Generic over the `wasmtime::Store` data type `T` — both
//! `HookStoreData` (chain-affecting) and `ObserverStoreData` (chain-
//! non-affecting) callers share one body, ensuring the bounds-check
//! invariant cannot drift between hosts.
use ;
/// Read `len` bytes from the calling wasm module's exported `memory`
/// at `ptr`, applying the cryptographer-pinned bounds-check contract.
/// All `(ptr, len)` host-fn arguments MUST pass through this helper
/// before the host touches wasm memory.
///
/// Generic over the `wasmtime::Store` data type `T` — both
/// `HookStoreData` (chain-affecting) and `ObserverStoreData` (chain-
/// non-affecting) callers share one body, ensuring the bounds-check
/// invariant cannot drift between hosts.
///
/// # Bounds-check rules (in order)
///
/// 1. `len < 0` → trap (wasm i32 reinterpreted as signed; negative
/// is an invalid byte count).
/// 2. `ptr < 0` → trap. Input validation runs BEFORE the `len == 0`
/// short-circuit so a hostile module passing `(ptr = -1, len = 0)`
/// is caught at the input-validation layer, not silently masked
/// (defense-in-depth ordering).
/// 3. `len == 0` → return `Ok(Vec::new())` without consulting the
/// memory export. A wasm caller asking for zero bytes is well-
/// formed even if the module has no `memory` export at all.
/// 4. Module has no `memory` export → trap (`memory` resolution fails).
/// 5. `ptr.checked_add(len)` overflows `u64` → trap (arithmetic
/// overflow defense).
/// 6. `ptr + len > Memory::data_size(&caller)` → OOB trap.
///
/// On success, the bytes are copied into a fresh `Vec<u8>` (host owns
/// the copy; wasm memory is not aliased).
///
/// # Why these checks
///
/// Without the bounds-check, a malicious or buggy wasm module could
/// pass `(ptr = SIZE_MAX, len = 1)` and induce the host to deref
/// arbitrary memory beyond the wasm linear-memory sandbox — an
/// FFI-shaped sandbox-escape primitive. Belt-and-braces alongside
/// wasmtime's own internal memory-region tracking.
//
// `dead_code` allowed: under `tier-2-observer-host-v2` only (no hook
// feature), this helper may be unused at the local call site. The
// function compiles + type-checks under the observer-only feature so
// the contract stays pinned in the shared module regardless of which
// host dispatch is currently active (drift avoidance).
pub
/// Write `bytes` to the calling wasm module's exported `memory` at
/// `ptr`, applying the symmetric bounds-check contract for output data
/// flow. Used by host-fns that write back into wasm memory (e.g.
/// `state.read` returning a looked-up value).
///
/// Generic over the `wasmtime::Store` data type `T` — same rationale
/// as [`read_caller_memory`].
///
/// # Bounds-check rules (mirroring [`read_caller_memory`])
///
/// - `ptr < 0` → trap.
/// - `bytes.is_empty()` → return `Ok(())` without consulting memory
/// export (zero-byte write is a no-op).
/// - Module has no `memory` export → trap.
/// - `ptr.checked_add(bytes.len() as u64)` overflows `u64` → trap.
/// - `ptr + len > Memory::data_size(&caller)` → OOB trap.
///
/// On success, the bytes are copied into wasm linear memory at `ptr`.
/// The host's `bytes` slice ownership is unchanged; wasm side observes
/// a fresh byte sequence at the supplied pointer.
//
// `dead_code` allowed: same rationale as `read_caller_memory`.
pub