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

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

View File

@@ -28,16 +28,63 @@ wasm_declare_vec!(byte);
#[allow(non_camel_case_types)]
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 {
let mut boxed_str: Box<str> = string.into_boxed_str();
let data = boxed_str.as_mut_ptr();
let size = boxed_str.bytes().len();
let wasm_name = Self { data, size };
let wasm_name = wasm_name_t { data, size };
Box::leak(boxed_str);
wasm_name
Self(wasm_name)
}
}

View File

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