Skip to content

Commit

Permalink
[feature] hyperledger-iroha#2511: Restrict FFI types on wasm (hyperle…
Browse files Browse the repository at this point in the history
…dger-iroha#2590)

Signed-off-by: Shanin Roman <shanin1000@yandex.ru>
  • Loading branch information
Erigara authored and mversic committed Sep 6, 2022
1 parent ccbfb53 commit be9176e
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 56 deletions.
3 changes: 3 additions & 0 deletions ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ version = "2.0.0-pre-rc.7"
authors = ["Iroha 2 team <https://github.com/orgs/soramitsu/teams/iroha2>"]
edition = "2021"

[features]
wasm = []

[dependencies]
iroha_ffi_derive = { version = "=2.0.0-pre-rc.7", path = "derive" }

Expand Down
63 changes: 55 additions & 8 deletions ffi/derive/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ fn variant_discriminants(enum_: &syn::DataEnum) -> Vec<syn::Expr> {
}

fn derive_try_from_repr_c_for_opaque_item_wrapper(name: &Ident) -> TokenStream2 {
let opaque_item_slice_into_ffi_derive = derive_try_from_repr_c_for_opaque_item_slice(name);
let opaque_item_slice_try_from_repr_c_derive =
derive_try_from_repr_c_for_opaque_item_slice(name);
let opaque_item_vec_try_from_repr_c_derive = derive_try_from_repr_c_for_opaque_item_vec(name);

quote! {
impl<'itm> iroha_ffi::TryFromReprC<'itm> for #name {
Expand Down Expand Up @@ -139,12 +141,15 @@ fn derive_try_from_repr_c_for_opaque_item_wrapper(name: &Ident) -> TokenStream2
}
}

#opaque_item_slice_into_ffi_derive
#opaque_item_slice_try_from_repr_c_derive
#opaque_item_vec_try_from_repr_c_derive
}
}

fn derive_try_from_repr_c_for_opaque_item(name: &Ident) -> TokenStream2 {
let opaque_item_slice_into_ffi_derive = derive_try_from_repr_c_for_opaque_item_slice(name);
let opaque_item_slice_try_from_repr_c_derive =
derive_try_from_repr_c_for_opaque_item_slice(name);
let opaque_item_vec_try_from_repr_c_derive = derive_try_from_repr_c_for_opaque_item_vec(name);

quote! {
impl<'itm> iroha_ffi::TryFromReprC<'itm> for #name {
Expand Down Expand Up @@ -187,7 +192,8 @@ fn derive_try_from_repr_c_for_opaque_item(name: &Ident) -> TokenStream2 {
}
}

#opaque_item_slice_into_ffi_derive
#opaque_item_slice_try_from_repr_c_derive
#opaque_item_vec_try_from_repr_c_derive
}
}

Expand All @@ -213,6 +219,29 @@ fn derive_try_from_repr_c_for_opaque_item_slice(name: &Ident) -> TokenStream2 {
}
}

fn derive_try_from_repr_c_for_opaque_item_vec(name: &Ident) -> TokenStream2 {
quote! {
impl<'itm> iroha_ffi::owned::TryFromReprCVec<'itm> for #name {
type Source = iroha_ffi::slice::SliceRef<'itm, <Self as iroha_ffi::TryFromReprC<'itm>>::Source>;
type Store = ();

unsafe fn try_from_repr_c(
source: Self::Source,
_: &'itm mut <Self as iroha_ffi::owned::TryFromReprCVec<'itm>>::Store,
) -> iroha_ffi::Result<Vec<Self>> {
let slice = source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull)?;
let mut res = Vec::with_capacity(slice.len());

for elem in slice {
res.push(iroha_ffi::TryFromReprC::try_from_repr_c(*elem, &mut ())?);
}

Ok(res)
}
}
}
}

fn derive_try_from_repr_c_for_item(_: &Ident) -> TokenStream2 {
quote! {
// TODO:
Expand Down Expand Up @@ -248,15 +277,17 @@ fn derive_try_from_repr_c_for_fieldless_enum(

quote! {
impl<'itm> iroha_ffi::TryFromReprC<'itm> for #enum_name {
type Source = #ffi_type;
type Source = <#ffi_type as iroha_ffi::TryFromReprC<'itm>>::Source;
type Store = ();

unsafe fn try_from_repr_c(
source: <Self as iroha_ffi::TryFromReprC<'itm>>::Source,
_: &mut <Self as iroha_ffi::TryFromReprC<'itm>>::Store
store: &mut <Self as iroha_ffi::TryFromReprC<'itm>>::Store
) -> iroha_ffi::Result<Self> {
#( #discriminants )*

let source: #ffi_type = iroha_ffi::TryFromReprC::try_from_repr_c(source, store)?;

match source {
#( #discriminant_names => Ok(#enum_name::#variant_names), )*
_ => Err(iroha_ffi::FfiReturn::TrapRepresentation),
Expand Down Expand Up @@ -323,6 +354,7 @@ fn derive_try_from_repr_c_for_fieldless_enum(

fn derive_into_ffi_for_opaque_item_wrapper(name: &Ident) -> TokenStream2 {
let opaque_item_slice_into_ffi_derive = derive_into_ffi_for_opaque_item_slice(name);
let opaque_item_vec_into_ffi_derive = derive_into_ffi_for_opaque_item_vec(name);

quote! {
impl iroha_ffi::IntoFfi for #name {
Expand Down Expand Up @@ -350,11 +382,13 @@ fn derive_into_ffi_for_opaque_item_wrapper(name: &Ident) -> TokenStream2 {
}

#opaque_item_slice_into_ffi_derive
#opaque_item_vec_into_ffi_derive
}
}

fn derive_into_ffi_for_opaque_item(name: &Ident) -> TokenStream2 {
let opaque_item_slice_into_ffi_derive = derive_into_ffi_for_opaque_item_slice(name);
let opaque_item_vec_into_ffi_derive = derive_into_ffi_for_opaque_item_vec(name);

quote! {
impl iroha_ffi::IntoFfi for #name {
Expand Down Expand Up @@ -388,6 +422,7 @@ fn derive_into_ffi_for_opaque_item(name: &Ident) -> TokenStream2 {
}

#opaque_item_slice_into_ffi_derive
#opaque_item_vec_into_ffi_derive
}
}

Expand All @@ -403,6 +438,18 @@ fn derive_into_ffi_for_opaque_item_slice(name: &Ident) -> TokenStream2 {
}
}

fn derive_into_ffi_for_opaque_item_vec(name: &Ident) -> TokenStream2 {
quote! {
impl iroha_ffi::owned::IntoFfiVec for #name {
type Target = iroha_ffi::owned::LocalSlice<<#name as iroha_ffi::IntoFfi>::Target>;

fn into_ffi(source: Vec<Self>) -> Self::Target {
source.into_iter().map(IntoFfi::into_ffi).collect()
}
}
}
}

fn derive_into_ffi_for_item(_: &Ident) -> TokenStream2 {
quote! {
// TODO:
Expand All @@ -414,10 +461,10 @@ fn derive_into_ffi_for_fieldless_enum(enum_name: &Ident, repr: &[syn::NestedMeta

quote! {
impl iroha_ffi::IntoFfi for #enum_name {
type Target = #ffi_type;
type Target = <#ffi_type as iroha_ffi::IntoFfi>::Target;

fn into_ffi(self) -> Self::Target {
self as #ffi_type
(self as #ffi_type).into_ffi()
}
}

Expand Down
6 changes: 4 additions & 2 deletions ffi/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ macro_rules! def_ffi_fn {
handle_id: $crate::handle::Id,
left_handle_ptr: *const core::ffi::c_void,
right_handle_ptr: *const core::ffi::c_void,
output_ptr: *mut u8,
// Pointer to FFI-safe representation of `bool` (u8 or u32 if `wasm` feature is active)
output_ptr: *mut <bool as $crate::IntoFfi>::Target,
) -> $crate::FfiReturn {
$crate::def_ffi_fn!(@catch_unwind {
use core::borrow::Borrow;
Expand Down Expand Up @@ -152,7 +153,8 @@ macro_rules! def_ffi_fn {
handle_id: $crate::handle::Id,
left_handle_ptr: *const core::ffi::c_void,
right_handle_ptr: *const core::ffi::c_void,
output_ptr: *mut i8,
// Pointer to FFI-safe representation of `Ordering` (i8 or i32 if `wasm` feature is active)
output_ptr: *mut <core::cmp::Ordering as $crate::IntoFfi>::Target,
) -> $crate::FfiReturn {
$crate::def_ffi_fn!(@catch_unwind {
use core::borrow::Borrow;
Expand Down
38 changes: 38 additions & 0 deletions ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

extern crate alloc;

use alloc::vec::Vec;

pub use iroha_ffi_derive::*;
use owned::Local;

Expand Down Expand Up @@ -107,6 +109,8 @@ pub trait Output: Sized {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i8)]
pub enum FfiReturn {
/// The input argument provided to FFI function can't be converted into inner rust representation.
ConversionFailed = -7,
/// The input argument provided to FFI function has a trap representation.
TrapRepresentation = -6,
/// FFI function execution panicked.
Expand Down Expand Up @@ -315,6 +319,40 @@ macro_rules! impl_tuple {
Local::new($ffi_ty($( <$ty as IntoFfi>::into_ffi($ty),)+))
}
}

impl<$($ty: IntoFfi),+> $crate::owned::IntoFfiVec for ($( $ty, )+) {
type Target = $crate::owned::LocalSlice<<Self as IntoFfi>::Target>;

fn into_ffi(source: Vec<Self>) -> Self::Target {
source.into_iter().map(IntoFfi::into_ffi).collect()
}
}

impl<'itm, $($ty: TryFromReprC<'itm>),+> $crate::owned::TryFromReprCVec<'itm> for ($( $ty, )+) {
type Source = $crate::slice::SliceRef<'itm, <Self as TryFromReprC<'itm>>::Source>;
type Store = Vec<<Self as TryFromReprC<'itm>>::Store>;

unsafe fn try_from_repr_c(
source: Self::Source,
store: &'itm mut Self::Store,
) -> Result<Vec<Self>> {
let prev_store_len = store.len();
let slice = source.into_rust().ok_or(FfiReturn::ArgIsNull)?;
store.extend(core::iter::repeat_with(Default::default).take(slice.len()));

let mut substore = &mut store[prev_store_len..];
let mut res = Vec::with_capacity(slice.len());

let mut i = 0;
while let Some((first, rest)) = substore.split_first_mut() {
res.push(TryFromReprC::try_from_repr_c(slice[i], first)?);
substore = rest;
i += 1;
}

Ok(res)
}
}
};

// NOTE: This is a trick to index tuples
Expand Down
90 changes: 57 additions & 33 deletions ffi/src/owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,45 @@ use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};

use crate::{
slice::{OutBoxedSlice, SliceRef},
AsReprCRef, FfiReturn, IntoFfi, Output, ReprC, TryFromReprC,
AsReprCRef, FfiReturn, IntoFfi, Output, ReprC, Result, TryFromReprC,
};

/// Trait that facilitates the implementation of [`IntoFfi`] for vectors of foreign types
pub trait IntoFfiVec: Sized {
/// Immutable vec equivalent of [`IntoFfi::Target`]
type Target: ReprC;

/// Convert from `&[Self]` into [`Self::Target`]
fn into_ffi(source: Vec<Self>) -> Self::Target;
}

/// Trait that facilitates the implementation of [`TryFromReprC`] for vector of foreign types
pub trait TryFromReprCVec<'slice>: Sized {
/// Vec equivalent of [`TryFromReprC::Source`]
type Source: ReprC + Copy;

/// Type into which state can be stored during conversion. Useful for returning
/// non-owning types but performing some conversion which requires allocation.
/// Serves similar purpose as does context in a closure
type Store: Default;

/// Convert from [`Self::Source`] into `&[Self]`
///
/// # Errors
///
/// * [`FfiReturn::ArgIsNull`] - given pointer is null
/// * [`FfiReturn::UnknownHandle`] - given id doesn't identify any known handle
/// * [`FfiReturn::TrapRepresentation`] - given value contains trap representation
///
/// # Safety
///
/// All conversions from a pointer must ensure pointer validity beforehand
unsafe fn try_from_repr_c(
source: Self::Source,
store: &'slice mut Self::Store,
) -> Result<Vec<Self>>;
}

/// Wrapper around `T` that is local to the conversion site. This structure carries
/// ownership and care must be taken not to let it transfer ownership into an FFI function
// NOTE: It's not possible to mutably reference local context
Expand Down Expand Up @@ -121,51 +157,42 @@ impl<T: ReprC> LocalSlice<T> {
}
}

impl<T: IntoFfi> IntoFfi for Vec<T>
impl<'itm, T> IntoFfiVec for &'itm T
where
T::Target: ReprC,
&'itm T: IntoFfi,
{
type Target = LocalSlice<T::Target>;
type Target = LocalSlice<<Self as IntoFfi>::Target>;

fn into_ffi(source: Vec<Self>) -> Self::Target {
source.into_iter().map(IntoFfi::into_ffi).collect()
}
}

impl<T: IntoFfiVec> IntoFfi for Vec<T> {
type Target = T::Target;

fn into_ffi(self) -> Self::Target {
self.into_iter().map(IntoFfi::into_ffi).collect()
<T as IntoFfiVec>::into_ffi(self)
}
}

impl<'itm, T: TryFromReprC<'itm>> TryFromReprC<'itm> for Vec<T> {
type Source = SliceRef<'itm, T::Source>;
type Store = Vec<T::Store>;
impl<'slice, T: TryFromReprCVec<'slice> + 'slice> TryFromReprC<'slice> for Vec<T> {
type Source = T::Source;
type Store = T::Store;

unsafe fn try_from_repr_c(
source: Self::Source,
store: &'itm mut Self::Store,
) -> Result<Self, FfiReturn> {
let prev_store_len = store.len();
let slice = source.into_rust().ok_or(FfiReturn::ArgIsNull)?;
store.extend(core::iter::repeat_with(Default::default).take(slice.len()));

let mut substore = &mut store[prev_store_len..];
let mut res = Vec::with_capacity(slice.len());

let mut i = 0;
while let Some((first, rest)) = substore.split_first_mut() {
res.push(<T as TryFromReprC<'itm>>::try_from_repr_c(slice[i], first)?);
substore = rest;
i += 1;
}

Ok(res)
store: &'slice mut Self::Store,
) -> Result<Self> {
<T as TryFromReprCVec>::try_from_repr_c(source, store)
}
}

impl<'itm> TryFromReprC<'itm> for String {
type Source = <Vec<u8> as TryFromReprC<'itm>>::Source;
type Store = ();

unsafe fn try_from_repr_c(
source: Self::Source,
_: &mut Self::Store,
) -> Result<Self, FfiReturn> {
unsafe fn try_from_repr_c(source: Self::Source, _: &mut Self::Store) -> Result<Self> {
String::from_utf8(source.into_rust().ok_or(FfiReturn::ArgIsNull)?.to_owned())
.map_err(|_e| FfiReturn::Utf8Error)
}
Expand All @@ -174,10 +201,7 @@ impl<'itm> TryFromReprC<'itm> for &'itm str {
type Source = <&'itm [u8] as TryFromReprC<'itm>>::Source;
type Store = ();

unsafe fn try_from_repr_c(
source: Self::Source,
_: &mut Self::Store,
) -> Result<Self, FfiReturn> {
unsafe fn try_from_repr_c(source: Self::Source, _: &mut Self::Store) -> Result<Self> {
core::str::from_utf8(source.into_rust().ok_or(FfiReturn::ArgIsNull)?)
.map_err(|_e| FfiReturn::Utf8Error)
}
Expand Down
Loading

0 comments on commit be9176e

Please sign in to comment.