@@ -1597,6 +1597,150 @@ fn add_host_functions(linker: &mut Linker<PluginState>) -> Result<(), WasmError>
15971597 state. last_broker_result . take ( )
15981598 } ) ?;
15991599
1600+ // ── Minimal WASI stubs ──────────────────────────────────────────────
1601+ // Some plugins (e.g. cel) compile with wasm32-wasip1 and import WASI
1602+ // functions even though Barbacane provides its own host ABI. We add
1603+ // lightweight stubs so the linker can resolve these imports.
1604+ // Full WASI support (wasmtime-wasi) can replace these if needed.
1605+
1606+ // random_get — deterministic bytes for HashMap seed initialisation.
1607+ // Acceptable in a sandboxed single-request context (no HashDoS risk).
1608+ linker
1609+ . func_wrap (
1610+ "wasi_snapshot_preview1" ,
1611+ "random_get" ,
1612+ |mut caller : Caller < ' _ , PluginState > , buf_ptr : i32 , buf_len : i32 | -> i32 {
1613+ let memory = match caller. get_export ( "memory" ) . and_then ( |e| e. into_memory ( ) ) {
1614+ Some ( m) => m,
1615+ None => return 1 ,
1616+ } ;
1617+ let start = buf_ptr as usize ;
1618+ let end = start + buf_len as usize ;
1619+ let data = memory. data_mut ( & mut caller) ;
1620+ if end > data. len ( ) {
1621+ return 1 ;
1622+ }
1623+ for ( i, byte) in data[ start..end] . iter_mut ( ) . enumerate ( ) {
1624+ * byte = ( i. wrapping_mul ( 0x9E3779B9 ) >> 24 ) as u8 ;
1625+ }
1626+ 0
1627+ } ,
1628+ )
1629+ . map_err ( |e| WasmError :: Instantiation ( format ! ( "wasi stub: {e}" ) ) ) ?;
1630+
1631+ // sched_yield — no-op, always succeeds.
1632+ linker
1633+ . func_wrap ( "wasi_snapshot_preview1" , "sched_yield" , || -> i32 { 0 } )
1634+ . map_err ( |e| WasmError :: Instantiation ( format ! ( "wasi stub: {e}" ) ) ) ?;
1635+
1636+ // clock_time_get — returns current time in nanoseconds.
1637+ linker
1638+ . func_wrap (
1639+ "wasi_snapshot_preview1" ,
1640+ "clock_time_get" ,
1641+ |mut caller : Caller < ' _ , PluginState > ,
1642+ _clock_id : i32 ,
1643+ _precision : i64 ,
1644+ time_ptr : i32 |
1645+ -> i32 {
1646+ let memory = match caller. get_export ( "memory" ) . and_then ( |e| e. into_memory ( ) ) {
1647+ Some ( m) => m,
1648+ None => return 1 ,
1649+ } ;
1650+ let nanos = std:: time:: SystemTime :: now ( )
1651+ . duration_since ( std:: time:: UNIX_EPOCH )
1652+ . map ( |d| d. as_nanos ( ) as u64 )
1653+ . unwrap_or ( 0 ) ;
1654+ let ptr = time_ptr as usize ;
1655+ let data = memory. data_mut ( & mut caller) ;
1656+ if ptr + 8 > data. len ( ) {
1657+ return 1 ;
1658+ }
1659+ data[ ptr..ptr + 8 ] . copy_from_slice ( & nanos. to_le_bytes ( ) ) ;
1660+ 0
1661+ } ,
1662+ )
1663+ . map_err ( |e| WasmError :: Instantiation ( format ! ( "wasi stub: {e}" ) ) ) ?;
1664+
1665+ // fd_write — silently discards output (plugins use host_log instead).
1666+ linker
1667+ . func_wrap (
1668+ "wasi_snapshot_preview1" ,
1669+ "fd_write" ,
1670+ |mut caller : Caller < ' _ , PluginState > ,
1671+ _fd : i32 ,
1672+ iovs_ptr : i32 ,
1673+ iovs_len : i32 ,
1674+ nwritten_ptr : i32 |
1675+ -> i32 {
1676+ let memory = match caller. get_export ( "memory" ) . and_then ( |e| e. into_memory ( ) ) {
1677+ Some ( m) => m,
1678+ None => return 1 ,
1679+ } ;
1680+ let data = memory. data_mut ( & mut caller) ;
1681+ let mut total: u32 = 0 ;
1682+ for i in 0 ..iovs_len as usize {
1683+ let base = ( iovs_ptr as usize ) + i * 8 ;
1684+ if base + 8 > data. len ( ) {
1685+ return 1 ;
1686+ }
1687+ let len =
1688+ u32:: from_le_bytes ( data[ base + 4 ..base + 8 ] . try_into ( ) . unwrap_or ( [ 0 ; 4 ] ) ) ;
1689+ total = total. saturating_add ( len) ;
1690+ }
1691+ let ptr = nwritten_ptr as usize ;
1692+ if ptr + 4 > data. len ( ) {
1693+ return 1 ;
1694+ }
1695+ data[ ptr..ptr + 4 ] . copy_from_slice ( & total. to_le_bytes ( ) ) ;
1696+ 0
1697+ } ,
1698+ )
1699+ . map_err ( |e| WasmError :: Instantiation ( format ! ( "wasi stub: {e}" ) ) ) ?;
1700+
1701+ // environ_get — no environment variables exposed.
1702+ linker
1703+ . func_wrap (
1704+ "wasi_snapshot_preview1" ,
1705+ "environ_get" ,
1706+ |_caller : Caller < ' _ , PluginState > , _environ : i32 , _environ_buf : i32 | -> i32 { 0 } ,
1707+ )
1708+ . map_err ( |e| WasmError :: Instantiation ( format ! ( "wasi stub: {e}" ) ) ) ?;
1709+
1710+ // environ_sizes_get — reports 0 vars, 0 bytes.
1711+ linker
1712+ . func_wrap (
1713+ "wasi_snapshot_preview1" ,
1714+ "environ_sizes_get" ,
1715+ |mut caller : Caller < ' _ , PluginState > , num_ptr : i32 , buf_size_ptr : i32 | -> i32 {
1716+ let memory = match caller. get_export ( "memory" ) . and_then ( |e| e. into_memory ( ) ) {
1717+ Some ( m) => m,
1718+ None => return 1 ,
1719+ } ;
1720+ let data = memory. data_mut ( & mut caller) ;
1721+ let np = num_ptr as usize ;
1722+ let bp = buf_size_ptr as usize ;
1723+ if np + 4 > data. len ( ) || bp + 4 > data. len ( ) {
1724+ return 1 ;
1725+ }
1726+ data[ np..np + 4 ] . copy_from_slice ( & 0u32 . to_le_bytes ( ) ) ;
1727+ data[ bp..bp + 4 ] . copy_from_slice ( & 0u32 . to_le_bytes ( ) ) ;
1728+ 0
1729+ } ,
1730+ )
1731+ . map_err ( |e| WasmError :: Instantiation ( format ! ( "wasi stub: {e}" ) ) ) ?;
1732+
1733+ // proc_exit — traps the module (should never be reached).
1734+ linker
1735+ . func_wrap (
1736+ "wasi_snapshot_preview1" ,
1737+ "proc_exit" ,
1738+ |_caller : Caller < ' _ , PluginState > , _code : i32 | {
1739+ // Intentional trap — WASM execution stops here
1740+ } ,
1741+ )
1742+ . map_err ( |e| WasmError :: Instantiation ( format ! ( "wasi stub: {e}" ) ) ) ?;
1743+
16001744 Ok ( ( ) )
16011745}
16021746
0 commit comments