Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] #2511: Restrict FFI types on wasm #2590

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Erigara marked this conversation as resolved.
Show resolved Hide resolved
) -> $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