2210: Fix memory leak in C API in some uses of `wasm_name_t` r=MarkMcCaskey a=MarkMcCaskey

This PR fixes a bug relating to `wasm_importtype_t` leaking memory, particularly on the path where `String` is converted into `wasm_name_t`. It fixes this by introducing a new type, `owned_wasm_name_t`, which is identical to `wasm_name_t` but has RAII and calls the destructor if it goes out of scope.

I'm not confident that the use of `owned_wasm_name_t` on the FFI boundary is correct though, we'll have to thoroughly review that before shipping this PR.

# Review

- [x] Add a short description of the the change to the CHANGELOG.md file


Co-authored-by: Mark McCaskey <mark@wasmer.io>
This commit is contained in:
bors[bot]
2021-03-26 15:39:09 +00:00
committed by GitHub
5 changed files with 81 additions and 22 deletions

View File

@@ -27,6 +27,7 @@
- [#2149](https://github.com/wasmerio/wasmer/pull/2144) `wasmer-engine-native` looks for clang-11 instead of clang-10. - [#2149](https://github.com/wasmerio/wasmer/pull/2144) `wasmer-engine-native` looks for clang-11 instead of clang-10.
### Fixed ### Fixed
- [#2210](https://github.com/wasmerio/wasmer/pull/2210) Fix a memory leak in the Wasm C API in the strings used to identify imports and exports coming from user code.
- [#2108](https://github.com/wasmerio/wasmer/pull/2108) The Object Native Engine generates code that now compiles correctly with C++. - [#2108](https://github.com/wasmerio/wasmer/pull/2108) The Object Native Engine generates code that now compiles correctly with C++.
- [#2125](https://github.com/wasmerio/wasmer/pull/2125) Fix RUSTSEC-2021-0023. - [#2125](https://github.com/wasmerio/wasmer/pull/2125) Fix RUSTSEC-2021-0023.
- [#2155](https://github.com/wasmerio/wasmer/pull/2155) Fix the implementation of shift and rotate in the LLVM compiler. - [#2155](https://github.com/wasmerio/wasmer/pull/2155) Fix the implementation of shift and rotate in the LLVM compiler.

View File

@@ -1,10 +1,10 @@
use super::{wasm_externtype_t, wasm_name_t}; use super::{owned_wasm_name_t, wasm_externtype_t, wasm_name_t};
use wasmer::ExportType; use wasmer::ExportType;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Clone)] #[derive(Clone)]
pub struct wasm_exporttype_t { pub struct wasm_exporttype_t {
name: Box<wasm_name_t>, name: owned_wasm_name_t,
extern_type: Box<wasm_externtype_t>, extern_type: Box<wasm_externtype_t>,
} }
@@ -12,11 +12,12 @@ wasm_declare_boxed_vec!(exporttype);
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_exporttype_new( pub extern "C" fn wasm_exporttype_new(
name: Option<Box<wasm_name_t>>, name: Option<&wasm_name_t>,
extern_type: Option<Box<wasm_externtype_t>>, extern_type: Option<Box<wasm_externtype_t>>,
) -> Option<Box<wasm_exporttype_t>> { ) -> Option<Box<wasm_exporttype_t>> {
let name = unsafe { owned_wasm_name_t::new(name?) };
Some(Box::new(wasm_exporttype_t { Some(Box::new(wasm_exporttype_t {
name: name?, name,
extern_type: extern_type?, extern_type: extern_type?,
})) }))
} }
@@ -42,7 +43,7 @@ impl From<ExportType> for wasm_exporttype_t {
impl From<&ExportType> for wasm_exporttype_t { impl From<&ExportType> for wasm_exporttype_t {
fn from(other: &ExportType) -> Self { fn from(other: &ExportType) -> Self {
let name: Box<wasm_name_t> = Box::new(other.name().to_string().into()); let name: owned_wasm_name_t = other.name().to_string().into();
let extern_type: Box<wasm_externtype_t> = Box::new(other.ty().into()); let extern_type: Box<wasm_externtype_t> = Box::new(other.ty().into());
wasm_exporttype_t { name, extern_type } wasm_exporttype_t { name, extern_type }

View File

@@ -1,11 +1,12 @@
use super::{wasm_externtype_t, wasm_name_t}; use super::{owned_wasm_name_t, wasm_externtype_t, wasm_name_t};
use wasmer::ImportType; use wasmer::ImportType;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Clone)] #[derive(Clone)]
#[repr(C)]
pub struct wasm_importtype_t { pub struct wasm_importtype_t {
module: Box<wasm_name_t>, module: owned_wasm_name_t,
name: Box<wasm_name_t>, name: owned_wasm_name_t,
extern_type: Box<wasm_externtype_t>, extern_type: Box<wasm_externtype_t>,
} }
@@ -13,13 +14,19 @@ wasm_declare_boxed_vec!(importtype);
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_importtype_new( pub extern "C" fn wasm_importtype_new(
module: Option<Box<wasm_name_t>>, module: Option<&wasm_name_t>,
name: Option<Box<wasm_name_t>>, name: Option<&wasm_name_t>,
extern_type: Option<Box<wasm_externtype_t>>, extern_type: Option<Box<wasm_externtype_t>>,
) -> Option<Box<wasm_importtype_t>> { ) -> Option<Box<wasm_importtype_t>> {
let (module, name) = unsafe {
(
owned_wasm_name_t::new(module?),
owned_wasm_name_t::new(name?),
)
};
Some(Box::new(wasm_importtype_t { Some(Box::new(wasm_importtype_t {
name: name?, name,
module: module?, module,
extern_type: extern_type?, extern_type: extern_type?,
})) }))
} }
@@ -50,8 +57,8 @@ impl From<ImportType> for wasm_importtype_t {
impl From<&ImportType> for wasm_importtype_t { impl From<&ImportType> for wasm_importtype_t {
fn from(other: &ImportType) -> Self { fn from(other: &ImportType) -> Self {
let module: Box<wasm_name_t> = Box::new(other.module().to_string().into()); let module: owned_wasm_name_t = other.module().to_string().into();
let name: Box<wasm_name_t> = Box::new(other.name().to_string().into()); let name: owned_wasm_name_t = other.name().to_string().into();
let extern_type: Box<wasm_externtype_t> = Box::new(other.ty().into()); let extern_type: Box<wasm_externtype_t> = Box::new(other.ty().into());
wasm_importtype_t { wasm_importtype_t {

View File

@@ -28,16 +28,63 @@ wasm_declare_vec!(byte);
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type wasm_name_t = wasm_byte_vec_t; pub type wasm_name_t = wasm_byte_vec_t;
impl From<String> for wasm_name_t { impl AsRef<wasm_name_t> for wasm_name_t {
fn as_ref(&self) -> &wasm_name_t {
&self
}
}
/// An owned version of `wasm_name_t`.
///
/// Assumes that data is either valid host-owned or null.
// NOTE: `wasm_name_t` already does a deep copy, so we just derive `Clone` here.
#[derive(Debug, Clone)]
#[repr(transparent)]
#[allow(non_camel_case_types)]
pub struct owned_wasm_name_t(wasm_name_t);
impl owned_wasm_name_t {
/// Take ownership of some `wasm_name_t`
///
/// # Safety
/// You must ensure that the data pointed to by `wasm_name_t` is valid and
/// that it is not owned by anyone else.
pub unsafe fn new(name: &wasm_name_t) -> Self {
Self(wasm_name_t {
size: name.size,
data: name.data,
})
}
}
impl Drop for owned_wasm_name_t {
fn drop(&mut self) {
if !self.0.data.is_null() {
let _v = unsafe { Vec::from_raw_parts(self.0.data, self.0.size, self.0.size) };
self.0.data = std::ptr::null_mut();
self.0.size = 0;
}
// why can't we call this function?
//unsafe { crate::wasm_c_api::macros::wasm_byte_vec_delete(Some(self.0)) }
}
}
impl AsRef<wasm_name_t> for owned_wasm_name_t {
fn as_ref(&self) -> &wasm_name_t {
&self.0
}
}
impl From<String> for owned_wasm_name_t {
fn from(string: String) -> Self { fn from(string: String) -> Self {
let mut boxed_str: Box<str> = string.into_boxed_str(); let mut boxed_str: Box<str> = string.into_boxed_str();
let data = boxed_str.as_mut_ptr(); let data = boxed_str.as_mut_ptr();
let size = boxed_str.bytes().len(); let size = boxed_str.bytes().len();
let wasm_name = Self { data, size }; let wasm_name = wasm_name_t { data, size };
Box::leak(boxed_str); Box::leak(boxed_str);
wasm_name Self(wasm_name)
} }
} }

View File

@@ -2,7 +2,10 @@
//! API. //! API.
use super::super::{ use super::super::{
externals::wasm_extern_t, module::wasm_module_t, store::wasm_store_t, types::wasm_name_t, externals::wasm_extern_t,
module::wasm_module_t,
store::wasm_store_t,
types::{owned_wasm_name_t, wasm_name_t},
wasi::wasi_env_t, wasi::wasi_env_t,
}; };
use crate::error::CApiError; use crate::error::CApiError;
@@ -19,8 +22,8 @@ use wasmer_wasi::{generate_import_object_from_env, get_wasi_version};
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Clone)] #[derive(Clone)]
pub struct wasmer_named_extern_t { pub struct wasmer_named_extern_t {
module: Box<wasm_name_t>, module: owned_wasm_name_t,
name: Box<wasm_name_t>, name: owned_wasm_name_t,
r#extern: Box<wasm_extern_t>, r#extern: Box<wasm_extern_t>,
} }
@@ -179,8 +182,8 @@ fn wasi_get_unordered_imports_inner(
*imports = import_object *imports = import_object
.into_iter() .into_iter()
.map(|((module, name), export)| { .map(|((module, name), export)| {
let module = Box::new(module.into()); let module = module.into();
let name = Box::new(name.into()); let name = name.into();
let extern_inner = Extern::from_vm_export(store, export); let extern_inner = Extern::from_vm_export(store, export);
Box::new(wasmer_named_extern_t { Box::new(wasmer_named_extern_t {