From d93ed759582becd44602aa647cfaa71d7e6945ec Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Thu, 2 Apr 2020 22:49:31 +0100 Subject: [PATCH] Allow passing label to `node_create` Ref. #630 --- docs/abi.md | 16 ++- examples/abitest/module_0/rust/src/lib.rs | 43 ++++++ oak/server/rust/oak_abi/src/lib.rs | 2 + .../rust/oak_runtime/src/node/wasm/mod.rs | 134 +++++++++++++----- .../rust/oak_runtime/src/node/wasm/tests.rs | 4 +- sdk/rust/oak/src/lib.rs | 27 +++- 6 files changed, 176 insertions(+), 50 deletions(-) diff --git a/docs/abi.md b/docs/abi.md index 1e640d533d7..7b81fa6aea6 100644 --- a/docs/abi.md +++ b/docs/abi.md @@ -176,11 +176,11 @@ return the channel handles for its read and write halves. ### node_create -`node_create: (usize, usize, usize, usize, u64) -> u32` creates a new Node -running the Node configuration identified by args 0 and 1, using the entrypoint -specified by args 2 and 3, passing in an initial handle to the read half of a -channel identified by arg 4. The entrypoint name is ignored when creating -non-WebAssembly Nodes. +`node_create: (usize, usize, usize, usize, usize, usize, u64) -> u32` creates a +new Node running the Node configuration identified by args 0 and 1, using the +entrypoint specified by args 2 and 3, passing in an initial handle to the read +half of a channel identified by arg 4. The entrypoint name is ignored when +creating non-WebAssembly Nodes. If creating the specified node would violate [information flow control](/docs/concepts.md#labels), returns @@ -188,9 +188,11 @@ If creating the specified node would violate - arg 0: Source buffer holding node configuration name - arg 1: Node configuration name size in bytes -- arg 2: Source buffer holding entrypoint name. +- arg 2: Source buffer holding entrypoint name - arg 3: Entrypoint name size in bytes -- arg 4: Handle to channel +- arg 4: Source buffer holding label +- arg 5: Label size in bytes +- arg 6: Handle to channel - return 0: Status of operation ### random_get diff --git a/examples/abitest/module_0/rust/src/lib.rs b/examples/abitest/module_0/rust/src/lib.rs index 2d1a8406723..947ea746796 100644 --- a/examples/abitest/module_0/rust/src/lib.rs +++ b/examples/abitest/module_0/rust/src/lib.rs @@ -23,6 +23,7 @@ use byteorder::WriteBytesExt; use expect::{expect, expect_eq, expect_matches}; use log::{debug, info}; use oak::{grpc, ChannelReadStatus, OakError, OakStatus}; +use oak_abi::label::Label; use prost::Message; use proto::{ AbiTestRequest, AbiTestResponse, GrpcTestRequest, GrpcTestResponse, OakAbiTestService, @@ -991,7 +992,41 @@ impl FrontendNode { let valid = "a_string"; let non_utf8_name: Vec = vec![0xc3, 0x28]; + let valid_label_bytes = Label::public_trusted().serialize(); + + // This sequence of bytes should not deserialize as a [`oak_abi::proto::policy::Label`] + // protobuf. We make sure here that this continues to be the case by making sure that + // [`Label::deserialize`] fails to parse these bytes. + let invalid_label_bytes = vec![0, 88, 0]; + assert_eq!(None, Label::deserialize(&invalid_label_bytes)); + unsafe { + expect_eq!( + OakStatus::Ok as u32, + oak_abi::node_create( + BACKEND_CONFIG_NAME.as_ptr(), + BACKEND_CONFIG_NAME.len(), + BACKEND_ENTRYPOINT_NAME.as_ptr(), + BACKEND_ENTRYPOINT_NAME.len(), + valid_label_bytes.as_ptr(), + valid_label_bytes.len(), + in_channel + ) + ); + + expect_eq!( + OakStatus::ErrInvalidArgs as u32, + oak_abi::node_create( + BACKEND_CONFIG_NAME.as_ptr(), + BACKEND_CONFIG_NAME.len(), + BACKEND_ENTRYPOINT_NAME.as_ptr(), + BACKEND_ENTRYPOINT_NAME.len(), + invalid_label_bytes.as_ptr(), + invalid_label_bytes.len(), + in_channel + ) + ); + expect_eq!( OakStatus::ErrInvalidArgs as u32, oak_abi::node_create( @@ -999,6 +1034,8 @@ impl FrontendNode { 1, valid.as_ptr(), valid.len(), + valid_label_bytes.as_ptr(), + valid_label_bytes.len(), in_channel ) ); @@ -1010,6 +1047,8 @@ impl FrontendNode { non_utf8_name.len(), valid.as_ptr(), valid.len(), + valid_label_bytes.as_ptr(), + valid_label_bytes.len(), in_channel ) ); @@ -1021,6 +1060,8 @@ impl FrontendNode { valid.len(), invalid_raw_offset() as *mut u8, 1, + valid_label_bytes.as_ptr(), + valid_label_bytes.len(), in_channel ) ); @@ -1032,6 +1073,8 @@ impl FrontendNode { valid.len(), non_utf8_name.as_ptr(), non_utf8_name.len(), + valid_label_bytes.as_ptr(), + valid_label_bytes.len(), in_channel ) ); diff --git a/oak/server/rust/oak_abi/src/lib.rs b/oak/server/rust/oak_abi/src/lib.rs index 85740effdad..decc5310c2b 100644 --- a/oak/server/rust/oak_abi/src/lib.rs +++ b/oak/server/rust/oak_abi/src/lib.rs @@ -144,6 +144,8 @@ extern "C" { config_len: usize, entrypoint_buf: *const u8, entrypoint_len: usize, + label_buf: *const u8, + label_len: usize, handle: u64, ) -> u32; diff --git a/oak/server/rust/oak_runtime/src/node/wasm/mod.rs b/oak/server/rust/oak_runtime/src/node/wasm/mod.rs index d2a2739476a..8e1b5768781 100644 --- a/oak/server/rust/oak_runtime/src/node/wasm/mod.rs +++ b/oak/server/rust/oak_runtime/src/node/wasm/mod.rs @@ -29,7 +29,7 @@ use byteorder::{ByteOrder, LittleEndian}; use rand::RngCore; use wasmi::ValueType; -use oak_abi::{ChannelReadStatus, OakStatus}; +use oak_abi::{label::Label, ChannelReadStatus, OakStatus}; use crate::{ pretty_name_for_thread, @@ -119,7 +119,8 @@ impl WasmInterface { .expect("WasmInterface memory not attached!?") } - /// Make sure a given address range falls within the currently allocated range of memory. + /// Validates whether a given address range falls within the currently allocated range of guest + /// memory. fn validate_ptr(&self, addr: AbiPointer, offset: AbiPointerOffset) -> Result<(), OakStatus> { let byte_size: wasmi::memory_units::Bytes = self.get_memory().current_size().into(); @@ -147,17 +148,20 @@ impl WasmInterface { (interface, handle) } - /// Corresponds to the host ABI function `node_create: (usize, usize, usize, usize, u64) -> - /// u32`. + /// Corresponds to the host ABI function [`node_create: (usize, usize, usize, usize, + /// u64) -> u32`](oak_abi::node_create). + #[allow(clippy::too_many_arguments)] fn node_create( &self, name_ptr: AbiPointer, name_length: AbiPointerOffset, entrypoint_ptr: AbiPointer, entrypoint_length: AbiPointerOffset, + label_ptr: AbiPointer, + label_length: AbiPointerOffset, initial_handle: AbiHandle, ) -> Result<(), OakStatus> { - let config_name = self + let config_name_bytes = self .get_memory() .get(name_ptr, name_length as usize) .map_err(|err| { @@ -167,18 +171,16 @@ impl WasmInterface { ); OakStatus::ErrInvalidArgs })?; - - let config_name = String::from_utf8(config_name).map_err(|err| { + let config_name = String::from_utf8(config_name_bytes).map_err(|err| { error!("node_create: Unable to parse config_name: {:?}", err); OakStatus::ErrInvalidArgs })?; - debug!( "{} node_create config_name is: {}", self.pretty_name, config_name ); - let entrypoint = self + let entrypoint_bytes = self .get_memory() .get(entrypoint_ptr, entrypoint_length as usize) .map_err(|err| { @@ -188,17 +190,31 @@ impl WasmInterface { ); OakStatus::ErrInvalidArgs })?; - - let entrypoint = String::from_utf8(entrypoint).map_err(|err| { + let entrypoint = String::from_utf8(entrypoint_bytes).map_err(|err| { error!("node_create: Unable to parse entrypoint: {:?}", err); OakStatus::ErrInvalidArgs })?; - debug!( "{} node_create entrypoint is: {}", self.pretty_name, entrypoint ); + let label_bytes = self + .get_memory() + .get(label_ptr, label_length as usize) + .map_err(|err| { + error!( + "node_create: Unable to read label from guest memory: {:?}", + err + ); + OakStatus::ErrInvalidArgs + })?; + let label = Label::deserialize(&label_bytes).ok_or_else(|| { + error!("node_create: could not deserialize label"); + OakStatus::ErrInvalidArgs + })?; + debug!("{} node_create label is: {:?}", self.pretty_name, label); + let channel_ref = self.readers.get(&initial_handle).ok_or(()).map_err(|_| { error!("node_create: Invalid handle"); OakStatus::ErrBadHandle @@ -206,13 +222,7 @@ impl WasmInterface { self.runtime .clone() - .node_create( - &config_name, - &entrypoint, - // TODO(#630): Let caller provide this label via the Wasm ABI. - &oak_abi::label::Label::public_trusted(), - channel_ref.clone(), - ) + .node_create(&config_name, &entrypoint, &label, channel_ref.clone()) .map_err(|_| { error!( "node_create: Config \"{}\" entrypoint \"{}\" not found", @@ -222,7 +232,8 @@ impl WasmInterface { }) } - /// Corresponds to the host ABI function `channel_create: (usize, usize) -> u32`. + /// Corresponds to the host ABI function [`channel_create: (usize, usize) -> + /// u32`](oak_abi::channel_create). fn channel_create( &mut self, write_addr: AbiPointer, @@ -231,7 +242,7 @@ impl WasmInterface { let (writer, reader) = self .runtime // TODO(#630): Let caller provide this label via the Wasm ABI. - .channel_create(&oak_abi::label::Label::public_trusted()); + .channel_create(&Label::public_trusted()); self.validate_ptr(write_addr, 8)?; self.validate_ptr(read_addr, 8)?; @@ -260,8 +271,8 @@ impl WasmInterface { }) } - /// Corresponds to the host ABI function `channel_write: (u64, usize, usize, usize, u32) -> - /// u32` + /// Corresponds to the host ABI function [`channel_write: (u64, usize, usize, usize, u32) -> + /// u32`](oak_abi::channel_write). fn channel_write( &self, writer_handle: AbiHandle, @@ -326,9 +337,9 @@ impl WasmInterface { Ok(()) } + /// Corresponds to the host ABI function [`channel_read: (u64, usize, usize, usize, usize, u32, + /// usize) -> u32`](oak_abi::channel_read). #[allow(clippy::too_many_arguments)] - /// Corresponds to the host ABI function `channel_read: (u64, usize, usize, usize, usize, u32, - /// usize) -> u32` fn channel_read( &mut self, reader_handle: AbiHandle, @@ -424,7 +435,8 @@ impl WasmInterface { } } - /// Corresponds to the host ABI function `random_get: (usize, usize) -> u32` + /// Corresponds to the host ABI function [`random_get: (usize, usize) -> + /// u32`](oak_abi::random_get). fn random_get(&self, dest: AbiPointer, dest_length: AbiPointerOffset) -> Result<(), OakStatus> { self.validate_ptr(dest, dest_length)?; @@ -438,7 +450,8 @@ impl WasmInterface { Ok(()) } - /// Corresponds to the host ABI function `wait_on_channels: (usize, u32) -> u32` + /// Corresponds to the host ABI function [`wait_on_channels: (usize, u32) -> + /// u32`](oak_abi::wait_on_channels). fn wait_on_channels( &mut self, status_buff: AbiPointer, @@ -523,7 +536,9 @@ impl wasmi::Externals for WasmInterface { let name_length: u32 = args.nth_checked(1)?; let entrypoint_ptr: u32 = args.nth_checked(2)?; let entrypoint_length: u32 = args.nth_checked(3)?; - let initial_handle: u64 = args.nth_checked(4)?; + let label_ptr: u32 = args.nth_checked(4)?; + let label_length: u32 = args.nth_checked(5)?; + let initial_handle: u64 = args.nth_checked(6)?; debug!( "{} node_create: {} {} {} {} {}", @@ -547,6 +562,8 @@ impl wasmi::Externals for WasmInterface { name_length, entrypoint_ptr, entrypoint_length, + label_ptr, + label_length, initial_handle, )) } @@ -684,13 +701,23 @@ impl wasmi::ModuleImportResolver for WasmInterface { field_name: &str, signature: &wasmi::Signature, ) -> Result { + // The comments alongside the types in the signatures correspond to the parameter names from + // /oak/server/rust/oak_abi/src/lib.rs let (index, sig) = match field_name { // - // - 0: node_create: (usize, usize, usize, usize, u64) -> u32 + // - 0: node_create: (usize, usize, usize, usize, usize, usize, u64) -> u32 "node_create" => ( NODE_CREATE, wasmi::Signature::new( - &[ABI_USIZE, ABI_USIZE, ABI_USIZE, ABI_USIZE, ABI_U64][..], + &[ + ABI_USIZE, // config_buf + ABI_USIZE, // config_len + ABI_USIZE, // entrypoint_buf + ABI_USIZE, // entrypoint_len + ABI_USIZE, // label_buf + ABI_USIZE, // label_len + ABI_U64, // handle + ][..], Some(ABI_U32), ), ), @@ -698,26 +725,49 @@ impl wasmi::ModuleImportResolver for WasmInterface { // - 1: random_get: (usize, usize) -> u32 "random_get" => ( RANDOM_GET, - wasmi::Signature::new(&[ABI_USIZE, ABI_USIZE][..], Some(ABI_U32)), + wasmi::Signature::new( + &[ + ABI_USIZE, // buf + ABI_USIZE, // len + ][..], + Some(ABI_U32), + ), ), // // - 2: channel_close: (u64) -> u32 "channel_close" => ( CHANNEL_CLOSE, - wasmi::Signature::new(&[ABI_U64][..], Some(ABI_U32)), + wasmi::Signature::new( + &[ + ABI_U64, // handle + ][..], + Some(ABI_U32), + ), ), // // - 3: channel_create: (usize, usize) -> u32 "channel_create" => ( CHANNEL_CREATE, - wasmi::Signature::new(&[ABI_USIZE, ABI_USIZE][..], Some(ABI_U32)), + wasmi::Signature::new( + &[ + ABI_USIZE, // write + ABI_USIZE, // read + ][..], + Some(ABI_U32), + ), ), // // - 4: channel_write: (u64, usize, usize, usize, u32) -> u32 "channel_write" => ( CHANNEL_WRITE, wasmi::Signature::new( - &[ABI_U64, ABI_USIZE, ABI_USIZE, ABI_USIZE, ABI_U32][..], + &[ + ABI_U64, // handle + ABI_USIZE, // buf + ABI_USIZE, // size + ABI_USIZE, // handle_buf + ABI_U32, // handle_count + ][..], Some(ABI_U32), ), ), @@ -727,7 +777,13 @@ impl wasmi::ModuleImportResolver for WasmInterface { CHANNEL_READ, wasmi::Signature::new( &[ - ABI_U64, ABI_USIZE, ABI_USIZE, ABI_USIZE, ABI_USIZE, ABI_U32, ABI_USIZE, + ABI_U64, // handle + ABI_USIZE, // buf + ABI_USIZE, // size + ABI_USIZE, // actual_size + ABI_USIZE, // handle_buf + ABI_U32, // handle_count + ABI_USIZE, // actual_handle_count ][..], Some(ABI_U32), ), @@ -736,7 +792,13 @@ impl wasmi::ModuleImportResolver for WasmInterface { // - 6: wait_on_channels: (usize, u32) -> u32 "wait_on_channels" => ( WAIT_ON_CHANNELS, - wasmi::Signature::new(&[ABI_USIZE, ABI_U32][..], Some(ABI_U32)), + wasmi::Signature::new( + &[ + ABI_USIZE, // buf + ABI_U32, // count + ][..], + Some(ABI_U32), + ), ), _ => { return Err(wasmi::Error::Instantiation(format!( diff --git a/oak/server/rust/oak_runtime/src/node/wasm/tests.rs b/oak/server/rust/oak_runtime/src/node/wasm/tests.rs index 27c33f79e12..373bcc6998b 100644 --- a/oak/server/rust/oak_runtime/src/node/wasm/tests.rs +++ b/oak/server/rust/oak_runtime/src/node/wasm/tests.rs @@ -19,6 +19,7 @@ use crate::{ node::Node, runtime::{Runtime, TEST_NODE_ID}, }; +use oak_abi::label::Label; use wat::{parse_file, parse_str}; fn setup_node>(buffer: S, entrypoint: &str) -> Box { @@ -28,8 +29,7 @@ fn setup_node>(buffer: S, entrypoint: &str) -> Box { entrypoint: entrypoint.to_string(), }; let runtime_ref = Arc::new(Runtime::create(configuration)); - let (_, reader_handle) = - runtime_ref.new_channel(TEST_NODE_ID, &oak_abi::label::Label::public_trusted()); + let (_, reader_handle) = runtime_ref.new_channel(TEST_NODE_ID, &Label::public_trusted()); let runtime_proxy = runtime_ref.new_runtime_proxy(); diff --git a/sdk/rust/oak/src/lib.rs b/sdk/rust/oak/src/lib.rs index 299747c7e6f..25a34d79666 100644 --- a/sdk/rust/oak/src/lib.rs +++ b/sdk/rust/oak/src/lib.rs @@ -19,7 +19,7 @@ use log::{debug, error, info}; use serde::{Deserialize, Serialize}; // Re-export ABI constants that are also visible as part of the SDK API. -pub use oak_abi::{ChannelReadStatus, OakStatus}; +pub use oak_abi::{label::Label, ChannelReadStatus, OakStatus}; mod error; pub use error::OakError; @@ -332,21 +332,38 @@ pub fn channel_close(handle: Handle) -> Result<(), OakStatus> { result_from_status(status as i32, ()) } -/// Create a new Node running the configuration identified by `config_name`, -/// running the entrypoint identified by `entrypoint_name` (for a Web Assembly -/// Node; this parameter is ignored when creating a pseudo-Node), passing it the -/// given handle. +/// Similar to [`node_create_with_label`], but with a fixed label corresponding to "public trusted". pub fn node_create( config_name: &str, entrypoint_name: &str, half: ReadHandle, ) -> Result<(), OakStatus> { + node_create_with_label(config_name, entrypoint_name, &Label::public_trusted(), half) +} + +/// Creates a new Node running the configuration identified by `config_name`, running the entrypoint +/// identified by `entrypoint_name` (for a Web Assembly Node; this parameter is ignored when +/// creating a pseudo-Node), with the provided `label`, and passing it the given handle. +/// +/// The provided label must be equal or more restrictive than the label of the calling node, i.e. +/// the label of the calling node must "flow to" the provided label. +/// +/// See https://github.com/project-oak/oak/blob/master/docs/concepts.md#labels +pub fn node_create_with_label( + config_name: &str, + entrypoint_name: &str, + label: &Label, + half: ReadHandle, +) -> Result<(), OakStatus> { + let label_bytes = label.serialize(); let status = unsafe { oak_abi::node_create( config_name.as_ptr(), config_name.len(), entrypoint_name.as_ptr(), entrypoint_name.len(), + label_bytes.as_ptr(), + label_bytes.len(), half.handle.id, ) };