From 5f08d6aad8c3078baf914c34dc5575bb8a48b3ed Mon Sep 17 00:00:00 2001 From: Ulyssa Date: Tue, 5 Nov 2024 14:12:03 -0800 Subject: [PATCH] Add stubs for shielding hostcalls --- crates/adapter/src/fastly/core.rs | 161 +++++++++++++++++++ lib/compute-at-edge-abi/compute-at-edge.witx | 1 + lib/compute-at-edge-abi/shielding.witx | 31 ++++ lib/data/viceroy-component-adapter.wasm | Bin 203259 -> 204537 bytes lib/src/component/mod.rs | 2 + lib/src/component/shielding.rs | 29 ++++ lib/src/linking.rs | 1 + lib/src/wiggle_abi.rs | 1 + lib/src/wiggle_abi/shielding.rs | 53 ++++++ lib/wit/deps/fastly/compute.wit | 26 +++ 10 files changed, 305 insertions(+) create mode 100644 lib/compute-at-edge-abi/shielding.witx create mode 100644 lib/src/component/shielding.rs create mode 100644 lib/src/wiggle_abi/shielding.rs diff --git a/crates/adapter/src/fastly/core.rs b/crates/adapter/src/fastly/core.rs index cb1e1c34..6495bfcc 100644 --- a/crates/adapter/src/fastly/core.rs +++ b/crates/adapter/src/fastly/core.rs @@ -3547,3 +3547,164 @@ pub mod fastly_purge { ) } } + +pub mod fastly_shielding { + use super::*; + use crate::bindings::fastly::api::{shielding as host, types}; + use std::slice; + + bitflags::bitflags! { + #[derive(Default)] + #[repr(transparent)] + pub struct ShieldBackendOptions: u32 { + const RESERVED = 1 << 0; + const CACHE_KEY = 1 << 1; + } + } + + #[repr(C)] + pub struct ShieldBackendConfig { + pub cache_key: *const u8, + pub cache_key_len: u32, + } + + impl Default for ShieldBackendConfig { + fn default() -> Self { + ShieldBackendConfig { + cache_key: std::ptr::null(), + cache_key_len: 0, + } + } + } + + #[export_name = "fastly_shielding#shield_info"] + pub fn shield_info( + name: *const u8, + name_len: usize, + info_block: *mut u8, + info_block_len: usize, + nwritten_out: *mut u32, + ) -> FastlyStatus { + let name = unsafe { slice::from_raw_parts(name, name_len) }; + with_buffer!( + info_block, + info_block_len, + { host::shield_info(name, u64::try_from(info_block_len).trapping_unwrap()) }, + |res| { + match res { + Ok(res) => { + unsafe { + *nwritten_out = u32::try_from(res.len()).unwrap_or(0); + } + std::mem::forget(res); + } + + Err(e) => { + if let types::Error::BufferLen(needed) = e { + unsafe { + *nwritten_out = u32::try_from(needed).unwrap_or(0); + } + } + + return Err(e.into()); + } + } + } + ) + } + + impl From for host::ShieldBackendOptionsMask { + fn from(value: ShieldBackendOptions) -> Self { + let mut flags = Self::empty(); + + flags.set( + Self::RESERVED, + value.contains(ShieldBackendOptions::RESERVED), + ); + flags.set( + Self::CACHE_KEY, + value.contains(ShieldBackendOptions::CACHE_KEY), + ); + + flags + } + } + + fn shield_backend_options( + mask: ShieldBackendOptions, + options: *const ShieldBackendConfig, + ) -> (host::ShieldBackendOptionsMask, host::ShieldBackendOptions) { + let mask = host::ShieldBackendOptionsMask::from(mask); + + // NOTE: this is only really safe because we never mutate the vectors -- we only need + // vectors to satisfy the interface produced by the DynamicBackendConfig record, + // `register_dynamic_backend` will never mutate the vectors it's given. + macro_rules! make_vec { + ($ptr_field:ident, $len_field:ident) => { + unsafe { + let len = usize::try_from((*options).$len_field).trapping_unwrap(); + Vec::from_raw_parts((*options).$ptr_field as *mut _, len, len) + } + }; + } + + let options = host::ShieldBackendOptions { + cache_key: if mask.contains(host::ShieldBackendOptionsMask::CACHE_KEY) { + make_vec!(cache_key, cache_key_len) + } else { + Vec::new() + }, + }; + + (mask, options) + } + + /// Turn a pop name into a backend that we can send requests to. + #[export_name = "fastly_shielding#backend_for_shield"] + pub fn backend_for_shield( + name: *const u8, + name_len: usize, + options_mask: ShieldBackendOptions, + options: *const ShieldBackendConfig, + backend_name: *mut u8, + backend_name_len: usize, + nwritten_out: *mut u32, + ) -> FastlyStatus { + let name = unsafe { slice::from_raw_parts(name, name_len) }; + let (mask, options) = shield_backend_options(options_mask, options); + with_buffer!( + backend_name, + backend_name_len, + { + let res = host::backend_for_shield( + name, + mask, + &options, + u64::try_from(backend_name_len).trapping_unwrap(), + ); + std::mem::forget(options); + res + }, + |res| { + match res { + Ok(res) => { + unsafe { + *nwritten_out = u32::try_from(res.len()).unwrap_or(0); + } + std::mem::forget(res); + } + + Err(e) => { + if let types::Error::BufferLen(needed) = e { + unsafe { + *nwritten_out = u32::try_from(needed).unwrap_or(0); + } + } + + return Err(e.into()); + } + } + } + ) + } +} diff --git a/lib/compute-at-edge-abi/compute-at-edge.witx b/lib/compute-at-edge-abi/compute-at-edge.witx index bb3e04e2..2526d369 100644 --- a/lib/compute-at-edge-abi/compute-at-edge.witx +++ b/lib/compute-at-edge-abi/compute-at-edge.witx @@ -2,6 +2,7 @@ (use "cache.witx") (use "config-store.witx") (use "http-cache.witx") +(use "shielding.witx") (module $fastly_abi (@interface func (export "init") diff --git a/lib/compute-at-edge-abi/shielding.witx b/lib/compute-at-edge-abi/shielding.witx new file mode 100644 index 00000000..8f0ca9de --- /dev/null +++ b/lib/compute-at-edge-abi/shielding.witx @@ -0,0 +1,31 @@ +(typename $shield_backend_options + (flags (@witx repr u32) + $reserved + $use_cache_key + )) + +(typename $shield_backend_config + (record + (field $cache_key (@witx pointer (@witx char8))) + (field $cache_key_len u32) + )) + +(module $fastly_shielding + + (@interface func (export "shield_info") + (param $name string) + (param $info_block (@witx pointer (@witx char8))) + (param $info_block_max_len (@witx usize)) + (result $err (expected $num_bytes (error $fastly_status))) + ) + + (@interface func (export "backend_for_shield") + (param $shield_name string) + (param $backend_config_mask $shield_backend_options) + (param $backend_configuration (@witx pointer $shield_backend_config)) + (param $backend_name_out (@witx pointer (@witx char8))) + (param $backend_name_max_len (@witx usize)) + (result $err (expected $num_bytes (error $fastly_status))) + ) + +) diff --git a/lib/data/viceroy-component-adapter.wasm b/lib/data/viceroy-component-adapter.wasm index 69946d188369d6b68c90bfb2fe453ef5ec345f78..87e20665fbf1794c6bd37db2440e2b646c1deacb 100755 GIT binary patch delta 40853 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 K4a4oTTK^x2B*vcr delta 39562 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

|z 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 E1^*LQJpcdz diff --git a/lib/src/component/mod.rs b/lib/src/component/mod.rs index ca8b0462..7212a469 100644 --- a/lib/src/component/mod.rs +++ b/lib/src/component/mod.rs @@ -64,6 +64,7 @@ pub fn link_host_functions(linker: &mut component::Linker) -> anyh fastly::api::object_store::add_to_linker(linker, |x| x)?; fastly::api::purge::add_to_linker(linker, |x| x)?; fastly::api::secret_store::add_to_linker(linker, |x| x)?; + fastly::api::shielding::add_to_linker(linker, |x| x)?; fastly::api::types::add_to_linker(linker, |x| x)?; fastly::api::uap::add_to_linker(linker, |x| x)?; @@ -91,5 +92,6 @@ pub mod log; pub mod object_store; pub mod purge; pub mod secret_store; +pub mod shielding; pub mod types; pub mod uap; diff --git a/lib/src/component/shielding.rs b/lib/src/component/shielding.rs new file mode 100644 index 00000000..e0e33ee8 --- /dev/null +++ b/lib/src/component/shielding.rs @@ -0,0 +1,29 @@ +use super::fastly::api::{shielding, types}; +use crate::linking::ComponentCtx; + +#[async_trait::async_trait] +impl shielding::Host for ComponentCtx { + async fn shield_info(&mut self, name: Vec, _max_len: u64) -> Result, types::Error> { + // Validate input name and return the unsupported error. + let _name = String::from_utf8(name)?; + + Err(types::Error::Unsupported) + } + + async fn backend_for_shield( + &mut self, + name: Vec, + options_mask: shielding::ShieldBackendOptionsMask, + options: shielding::ShieldBackendOptions, + _max_len: u64, + ) -> Result, types::Error> { + // Validate our inputs and return the unsupported error. + let _target_shield = String::from_utf8(name)?; + + if options_mask.contains(shielding::ShieldBackendOptionsMask::CACHE_KEY) { + let _ = String::from_utf8(options.cache_key)?; + } + + Err(types::Error::Unsupported) + } +} diff --git a/lib/src/linking.rs b/lib/src/linking.rs index 2033fa71..b7ee6d25 100644 --- a/lib/src/linking.rs +++ b/lib/src/linking.rs @@ -306,6 +306,7 @@ pub fn link_host_functions( wiggle_abi::fastly_object_store::add_to_linker(linker, WasmCtx::session)?; wiggle_abi::fastly_purge::add_to_linker(linker, WasmCtx::session)?; wiggle_abi::fastly_secret_store::add_to_linker(linker, WasmCtx::session)?; + wiggle_abi::fastly_shielding::add_to_linker(linker, WasmCtx::session)?; wiggle_abi::fastly_uap::add_to_linker(linker, WasmCtx::session)?; link_legacy_aliases(linker)?; Ok(()) diff --git a/lib/src/wiggle_abi.rs b/lib/src/wiggle_abi.rs index 46c6bb88..cd4951f8 100644 --- a/lib/src/wiggle_abi.rs +++ b/lib/src/wiggle_abi.rs @@ -68,6 +68,7 @@ mod obj_store_impl; mod req_impl; mod resp_impl; mod secret_store_impl; +mod shielding; mod uap_impl; // Expand the `.witx` interface definition into a collection of modules. The `types` module will diff --git a/lib/src/wiggle_abi/shielding.rs b/lib/src/wiggle_abi/shielding.rs new file mode 100644 index 00000000..19fa90ab --- /dev/null +++ b/lib/src/wiggle_abi/shielding.rs @@ -0,0 +1,53 @@ +use crate::error::Error; +use crate::session::Session; +use crate::wiggle_abi::{fastly_shielding, types}; + +impl fastly_shielding::FastlyShielding for Session { + fn shield_info( + &mut self, + memory: &mut wiggle::GuestMemory<'_>, + name: wiggle::GuestPtr, + _out_buffer: wiggle::GuestPtr, + _out_buffer_max_len: u32, + ) -> Result { + // Validate the input name and then return the unsupported error. + let name_bytes = memory.to_vec(name.as_bytes())?; + let _name = String::from_utf8(name_bytes).map_err(|_| Error::InvalidArgument)?; + + Err(Error::Unsupported { + msg: "shielding hostcalls are not supported", + }) + } + + fn backend_for_shield( + &mut self, + memory: &mut wiggle::GuestMemory<'_>, + shield_name: wiggle::GuestPtr, + shield_backend_options: types::ShieldBackendOptions, + shield_backend_config: wiggle::GuestPtr, + _out_buffer: wiggle::GuestPtr, + _out_buffer_max_len: u32, + ) -> Result { + // Validate our inputs and then return the unsupported error. + let Some(_) = memory.as_str(shield_name)?.map(str::to_string) else { + return Err(Error::ValueAbsent); + }; + + if shield_backend_options.contains(types::ShieldBackendOptions::RESERVED) { + return Err(Error::InvalidArgument); + } + + let config = memory.read(shield_backend_config)?; + + if shield_backend_options.contains(types::ShieldBackendOptions::USE_CACHE_KEY) { + let field_string = config.cache_key.as_array(config.cache_key_len).cast(); + if memory.as_str(field_string)?.is_none() { + return Err(Error::InvalidArgument); + } + } + + Err(Error::Unsupported { + msg: "shielding hostcalls are not supported", + }) + } +} diff --git a/lib/wit/deps/fastly/compute.wit b/lib/wit/deps/fastly/compute.wit index a336c344..ba8afb06 100644 --- a/lib/wit/deps/fastly/compute.wit +++ b/lib/wit/deps/fastly/compute.wit @@ -1334,6 +1334,31 @@ interface config-store { ) -> result>, error>; } +interface shielding { + use types.{error}; + + shield-info: func( + name: list, + max-len: u64, + ) -> result, error>; + + flags shield-backend-options-mask { + reserved, + cache-key, + } + + record shield-backend-options { + cache-key: list, + } + + backend-for-shield: func( + name: list, + options-mask: shield-backend-options-mask, + options: shield-backend-options, + max-len: u64, + ) -> result, error>; +} + interface reactor { use http-types.{request-handle, body-handle}; @@ -1381,6 +1406,7 @@ world compute { import object-store; import purge; import secret-store; + import shielding; import config-store; import uap;