Lines
69.7 %
Functions
41.67 %
Branches
100 %
//! Host output channel for the eval-mode module.
//!
//! PRINT / DISPLAY / NEWLINE / DEBUG lower to a call into the `env.log`
//! import (`(i32 level, i32 ptr, i32 len) -> ()`), reading the message from
//! the guest's exported linear memory. Script mode wires this in
//! `scripting::host`; the rpc Session path needs the same import or those
//! natives can't compile. Output is routed to `tracing` (the rpc channel has
//! no separate stdout) — same sink and level mapping as the script-mode host.
use wasmtime::{Caller, Extern, Linker};
use crate::session::SessionData;
pub fn register(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
linker.func_wrap(
"env",
"log",
|mut caller: Caller<'_, SessionData>, level: u32, ptr: u32, len: u32| {
let Some(Extern::Memory(memory)) = caller.get_export("memory") else {
return;
};
let data = memory.data(&caller);
// `ptr`/`len` are guest-controlled; compute the end with a checked
// add so a hostile/overflowing range is rejected (not wrapped in
// release / panicked in debug) before the slice.
let start = ptr as usize;
let Some(end) = start.checked_add(len as usize) else {
let Some(bytes) = data.get(start..end) else {
let Ok(msg) = std::str::from_utf8(bytes) else {
// Capture into the per-request buffer (for the SLYNK mREPL's
// `:write-string`) AND tee to tracing (the sshd / text-REPL paths,
// which don't drain the buffer, still see it in logs).
caller.data().push_output(msg);
match level {
0 => tracing::debug!("[script] {msg}"),
1 => tracing::info!("[script] {msg}"),
2 => tracing::warn!("[script] {msg}"),
_ => tracing::error!("[script] {msg}"),
}
},
)?;
Ok(())
/// Links a NO-OP `env.log` for the render surface. Eval-mode modules always
/// import `env.log` (PRINT/DISPLAY/DEBUG lower to it), so the import must
/// resolve — but a template is untrusted source with no legitimate output
/// channel, so render discards the message entirely: no per-request buffer
/// append, no `tracing` tee. This closes the log-flood / unbounded-host-memory
/// side channel a real `env.log` would hand an attacker-controlled template.
pub fn register_silent(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
|_caller: Caller<'_, SessionData>, _level: u32, _ptr: u32, _len: u32| {},