mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 16:42:21 -05:00
fix(ext/ffi): disallow empty ffi structs (#17487)
This patch makes `NativeType` to `libffi::middle::Type` conversion failliable and w.t disallows struct with empty fields. libffi does not handle "empty" struct because they don't exist in C (or Rust). Fixes #17481
This commit is contained in:
parent
638b6ef554
commit
5928925541
5 changed files with 71 additions and 18 deletions
|
@ -278,7 +278,7 @@ where
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
let symbol = PtrSymbol::new(pointer, &def);
|
let symbol = PtrSymbol::new(pointer, &def)?;
|
||||||
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
|
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
|
||||||
let def_result = def.result.clone();
|
let def_result = def.result.clone();
|
||||||
|
|
||||||
|
@ -379,7 +379,7 @@ where
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
let symbol = PtrSymbol::new(pointer, &def);
|
let symbol = PtrSymbol::new(pointer, &def)?;
|
||||||
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
|
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
|
||||||
|
|
||||||
let out_buffer = out_buffer
|
let out_buffer = out_buffer
|
||||||
|
|
|
@ -40,18 +40,19 @@ pub struct PtrSymbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PtrSymbol {
|
impl PtrSymbol {
|
||||||
pub fn new(fn_ptr: usize, def: &ForeignFunction) -> Self {
|
pub fn new(fn_ptr: usize, def: &ForeignFunction) -> Result<Self, AnyError> {
|
||||||
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
|
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
|
||||||
let cif = libffi::middle::Cif::new(
|
let cif = libffi::middle::Cif::new(
|
||||||
def
|
def
|
||||||
.parameters
|
.parameters
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(libffi::middle::Type::from),
|
.map(libffi::middle::Type::try_from)
|
||||||
def.result.clone().into(),
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
def.result.clone().try_into()?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self { cif, ptr }
|
Ok(Self { cif, ptr })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,8 +579,12 @@ where
|
||||||
waker: None,
|
waker: None,
|
||||||
}));
|
}));
|
||||||
let cif = Cif::new(
|
let cif = Cif::new(
|
||||||
args.parameters.into_iter().map(libffi::middle::Type::from),
|
args
|
||||||
libffi::middle::Type::from(args.result),
|
.parameters
|
||||||
|
.into_iter()
|
||||||
|
.map(libffi::middle::Type::try_from)
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
libffi::middle::Type::try_from(args.result)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
// SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists.
|
// SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists.
|
||||||
|
|
|
@ -166,8 +166,9 @@ where
|
||||||
.parameters
|
.parameters
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(libffi::middle::Type::from),
|
.map(libffi::middle::Type::try_from)
|
||||||
foreign_fn.result.clone().into(),
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
foreign_fn.result.clone().try_into()?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let func_key = v8::String::new(scope, &symbol_key).unwrap();
|
let func_key = v8::String::new(scope, &symbol_key).unwrap();
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::error::type_error;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
|
||||||
/// Defines the accepted types that can be used as
|
/// Defines the accepted types that can be used as
|
||||||
/// parameters and return values in FFI.
|
/// parameters and return values in FFI.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq)]
|
||||||
|
@ -25,9 +28,11 @@ pub enum NativeType {
|
||||||
Struct(Box<[NativeType]>),
|
Struct(Box<[NativeType]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NativeType> for libffi::middle::Type {
|
impl TryFrom<NativeType> for libffi::middle::Type {
|
||||||
fn from(native_type: NativeType) -> Self {
|
type Error = AnyError;
|
||||||
match native_type {
|
|
||||||
|
fn try_from(native_type: NativeType) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match native_type {
|
||||||
NativeType::Void => libffi::middle::Type::void(),
|
NativeType::Void => libffi::middle::Type::void(),
|
||||||
NativeType::U8 | NativeType::Bool => libffi::middle::Type::u8(),
|
NativeType::U8 | NativeType::Bool => libffi::middle::Type::u8(),
|
||||||
NativeType::I8 => libffi::middle::Type::i8(),
|
NativeType::I8 => libffi::middle::Type::i8(),
|
||||||
|
@ -44,10 +49,18 @@ impl From<NativeType> for libffi::middle::Type {
|
||||||
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
|
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
|
||||||
libffi::middle::Type::pointer()
|
libffi::middle::Type::pointer()
|
||||||
}
|
}
|
||||||
NativeType::Struct(fields) => libffi::middle::Type::structure(
|
NativeType::Struct(fields) => {
|
||||||
fields.iter().map(|field| field.clone().into()),
|
libffi::middle::Type::structure(match fields.len() > 0 {
|
||||||
),
|
true => fields
|
||||||
}
|
.iter()
|
||||||
|
.map(|field| field.clone().try_into())
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
false => {
|
||||||
|
return Err(type_error("Struct must have at least one field"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,40 @@ assertThrows(
|
||||||
"Failed to register symbol non_existent_symbol",
|
"Failed to register symbol non_existent_symbol",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assertThrows(() => {
|
||||||
|
Deno.dlopen(libPath, {
|
||||||
|
print_something: {
|
||||||
|
parameters: [],
|
||||||
|
result: { struct: [] }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
TypeError,
|
||||||
|
"Struct must have at least one field"
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThrows(() => {
|
||||||
|
Deno.dlopen(libPath, {
|
||||||
|
print_something: {
|
||||||
|
parameters: [ { struct: [] } ],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
TypeError,
|
||||||
|
"Struct must have at least one field"
|
||||||
|
});
|
||||||
|
|
||||||
|
const Empty = { struct: [] }
|
||||||
|
assertThrows(() => {
|
||||||
|
Deno.dlopen(libPath, {
|
||||||
|
print_something: {
|
||||||
|
parameters: [ { struct: [Empty] } ],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
TypeError,
|
||||||
|
"Struct must have at least one field"
|
||||||
|
});
|
||||||
|
|
||||||
const Point = ["f64", "f64"];
|
const Point = ["f64", "f64"];
|
||||||
const Size = ["f64", "f64"];
|
const Size = ["f64", "f64"];
|
||||||
const Rect = ["f64", "f64", "f64", "f64"];
|
const Rect = ["f64", "f64", "f64", "f64"];
|
||||||
|
@ -720,4 +754,4 @@ function testOptimized(fn, callback) {
|
||||||
console.log(r2);
|
console.log(r2);
|
||||||
}
|
}
|
||||||
assertIsOptimized(fn);
|
assertIsOptimized(fn);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue