Improve ffiref (#243)

This commit is contained in:
qwreey 2024-09-02 15:11:13 +00:00
parent 94d8d079c4
commit e23aaef8a7
No known key found for this signature in database
GPG key ID: D28DB79297A214BD
11 changed files with 129 additions and 75 deletions

View file

@ -10,7 +10,7 @@ use super::{
};
use crate::ffi::{
ffi_association::{get_association, set_association},
FfiBox, GetNativeDataHandle, NativeConvert, NativeDataHandle, NativeSignedness, NativeSize,
FfiBox, GetNativeDataHandle, NativeConvert, NativeDataHandle, NativeSize,
};
// This is a series of some type.
@ -104,11 +104,6 @@ impl NativeSize for CArr {
self.size
}
}
impl NativeSignedness for CArr {
fn get_signedness(&self) -> bool {
false
}
}
impl NativeConvert for CArr {
// FIXME: FfiBox, FfiRef support required
unsafe fn luavalue_into<'lua>(
@ -192,7 +187,7 @@ impl LuaUserData for CArr {
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.check_readable(&userdata, offset, this.get_size()) {
if !data_handle.check_readable(offset, this.get_size()) {
return Err(LuaError::external("Unreadable data handle"));
}
@ -208,7 +203,7 @@ impl LuaUserData for CArr {
if !data_handle.check_boundary(offset, this.size) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.checek_writable(&userdata, offset, this.size) {
if !data_handle.checek_writable(offset, this.size) {
return Err(LuaError::external("Unwritable data handle"));
}

View file

@ -1,7 +1,10 @@
use libffi::middle::{Cif, Type};
use mlua::prelude::*;
use super::c_helper::{libffi_type_from_userdata, libffi_type_list_from_table};
use super::c_helper::{
get_conv, get_conv_list_from_table, libffi_type_from_userdata, libffi_type_list_from_table,
};
use crate::ffi::NativeConvert;
// cfn is a type declaration for a function.
// Basically, when calling an external function, this type declaration
@ -21,24 +24,35 @@ use super::c_helper::{libffi_type_from_userdata, libffi_type_list_from_table};
pub struct CFn {
libffi_cif: Cif,
args: Vec<Type>,
ret: Type,
args_conv: Vec<*const dyn NativeConvert>,
ret_conv: *const dyn NativeConvert,
}
impl CFn {
pub fn new(args: Vec<Type>, ret: Type) -> Self {
let libffi_cif = Cif::new(args.clone(), ret.clone());
pub fn new(
args: Vec<Type>,
ret: Type,
args_conv: Vec<*const dyn NativeConvert>,
ret_conv: *const dyn NativeConvert,
) -> Self {
let libffi_cif: Cif = Cif::new(args.clone(), ret.clone());
Self {
libffi_cif,
args,
ret,
args_conv,
ret_conv,
}
}
pub fn new_from_lua_table(lua: &Lua, args: LuaTable, ret: LuaAnyUserData) -> LuaResult<Self> {
let args = libffi_type_list_from_table(lua, &args)?;
let ret = libffi_type_from_userdata(lua, &ret)?;
Ok(Self::new(args, ret))
let args_type = libffi_type_list_from_table(lua, &args)?;
let ret_type = libffi_type_from_userdata(lua, &ret)?;
Ok(Self::new(
args_type,
ret_type,
unsafe { get_conv_list_from_table(&args)? },
unsafe { get_conv(&ret)? },
))
}
}

View file

@ -50,6 +50,7 @@ impl CStruct {
// Get tailing padded size of struct
// See http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html
// In here, using get_ensured_size is waste
let size = unsafe { (*struct_type.as_raw_ptr()).size };
Ok(Self {
@ -91,7 +92,8 @@ impl CStruct {
}
// size of
result.push_str(format!("size = {} ", userdata.borrow::<CStruct>()?.size).as_str());
result
.push_str(format!("size = {} ", userdata.borrow::<CStruct>()?.get_size()).as_str());
Ok(result)
} else {
Err(LuaError::external("failed to get inner type table."))
@ -206,7 +208,7 @@ impl LuaUserData for CStruct {
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.check_readable(&userdata, offset, this.get_size()) {
if !data_handle.check_readable(offset, this.get_size()) {
return Err(LuaError::external("Unreadable data handle"));
}
@ -219,10 +221,10 @@ impl LuaUserData for CStruct {
let offset = offset.unwrap_or(0);
let data_handle = &userdata.get_data_handle()?;
if !data_handle.check_boundary(offset, this.size) {
if !data_handle.check_boundary(offset, this.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.checek_writable(&userdata, offset, this.size) {
if !data_handle.checek_writable(offset, this.get_size()) {
return Err(LuaError::external("Unwritable data handle"));
}

View file

@ -164,7 +164,7 @@ where
if !data_handle.check_boundary(offset, ctype.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.check_readable(&userdata, offset, ctype.get_size()) {
if !data_handle.check_readable(offset, ctype.get_size()) {
return Err(LuaError::external("Unreadable data handle"));
}
@ -187,7 +187,7 @@ where
if !data_handle.check_boundary(offset, ctype.get_size()) {
return Err(LuaError::external("Out of bounds"));
}
if !data_handle.checek_writable(&userdata, offset, ctype.get_size()) {
if !data_handle.checek_writable(offset, ctype.get_size()) {
return Err(LuaError::external("Unwritable data handle"));
}

View file

@ -0,0 +1 @@
use core::ffi::c_void;

View file

@ -1,27 +1,17 @@
use std::boxed::Box;
use std::sync::OnceLock;
use mlua::prelude::*;
use super::{
association_names::REF_INNER,
ffi_association::set_association,
ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag, FfiRefFlagList},
ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag, FfiRefFlagList, UNSIZED_BOUNDS},
NativeDataHandle,
};
static BOX_REF_FLAGS: OnceLock<FfiRefFlagList> = OnceLock::new();
fn get_box_ref_flags() -> FfiRefFlagList {
BOX_REF_FLAGS
.get_or_init(|| {
FfiRefFlagList::new(&[
FfiRefFlag::Offsetable,
FfiRefFlag::Readable,
FfiRefFlag::Writable,
])
})
.to_owned()
}
const BOX_REF_FLAGS: FfiRefFlagList = FfiRefFlagList::new(
FfiRefFlag::Offsetable.value() | FfiRefFlag::Readable.value() | FfiRefFlag::Writable.value(),
);
// It is an untyped, sized memory area that Lua can manage.
// This area is safe within Lua. Operations have their boundaries checked.
@ -35,6 +25,7 @@ fn get_box_ref_flags() -> FfiRefFlagList {
struct RefData {
address: usize,
offset: usize,
lua_inner_id: i32,
}
pub struct FfiBox {
@ -77,7 +68,7 @@ impl FfiBox {
this: LuaAnyUserData<'lua>,
offset: Option<isize>,
) -> LuaResult<LuaAnyUserData<'lua>> {
let mut target = this.borrow_mut::<FfiBox>()?;
let target = this.borrow::<FfiBox>()?;
let mut bounds = FfiRefBounds::new(0, target.size());
let mut ptr = target.get_ptr();
@ -98,7 +89,7 @@ impl FfiBox {
// To deref a box space is to allow lua to read any space,
// which has security issues and is ultimately dangerous.
// Therefore, box:ref():deref() is not allowed.
let luaref = lua.create_userdata(FfiRef::new(ptr.cast(), get_box_ref_flags(), bounds))?;
let luaref = lua.create_userdata(FfiRef::new(ptr.cast(), BOX_REF_FLAGS, bounds))?;
// Makes box alive longer then ref
set_association(lua, REF_INNER, &luaref, &this)?;
@ -106,6 +97,27 @@ impl FfiBox {
Ok(luaref)
}
// Make FfiRef from box, without any safe features
pub fn luaref_unsafe<'lua>(
lua: &'lua Lua,
this: LuaAnyUserData<'lua>,
offset: Option<isize>,
) -> LuaResult<LuaAnyUserData<'lua>> {
let target = this.borrow::<FfiBox>()?;
let mut ptr = target.get_ptr();
// Calculate offset
if let Some(t) = offset {
ptr = unsafe { target.get_ptr().byte_offset(t) };
}
lua.create_userdata(FfiRef::new(
ptr.cast(),
FfiRefFlagList::all(),
UNSIZED_BOUNDS,
))
}
// Fill every field with 0
pub fn zero(&mut self) {
self.data.fill(0u8);
@ -130,13 +142,16 @@ impl NativeDataHandle for FfiBox {
self.size() > ((offset as usize) + size)
}
// FIXME
fn checek_writable(&self, userdata: &LuaAnyUserData, offset: isize, size: usize) -> bool {
fn checek_writable(&self, offset: isize, size: usize) -> bool {
true
}
// FIXME
fn check_readable(&self, userdata: &LuaAnyUserData, offset: isize, size: usize) -> bool {
fn check_readable(&self, offset: isize, size: usize) -> bool {
true
}
fn mark_ref(&self, userdata: &LuaAnyUserData, offset: isize, ptr: usize) -> LuaResult<()> {
Ok(())
}
unsafe fn get_pointer(&self, offset: isize) -> *mut () {
self.get_ptr().byte_offset(offset).cast::<()>()
}
@ -155,8 +170,13 @@ impl LuaUserData for FfiBox {
methods.add_function(
"ref",
|lua, (this, offset): (LuaAnyUserData, Option<isize>)| {
let luaref = FfiBox::luaref(lua, this, offset)?;
Ok(luaref)
FfiBox::luaref(lua, this, offset)
},
);
methods.add_function(
"unsafeRef",
|lua, (this, offset): (LuaAnyUserData, Option<isize>)| {
FfiBox::luaref_unsafe(lua, this, offset)
},
);
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(this.stringify()));

View file

@ -1,3 +0,0 @@
struct FfiFunc {
args: Vec,
}

View file

@ -1,5 +1,4 @@
use std::ffi::c_void;
use std::sync::LazyLock;
use core::ffi::c_void;
use dlopen2::symbor::Library;
use mlua::prelude::*;
@ -10,14 +9,12 @@ use super::{
ffi_ref::{FfiRef, FfiRefFlag, FfiRefFlagList, UNSIZED_BOUNDS},
};
static LIB_REF_FLAGS: LazyLock<FfiRefFlagList> = LazyLock::new(|| {
FfiRefFlagList::new(&[
FfiRefFlag::Offsetable,
FfiRefFlag::Readable,
FfiRefFlag::Dereferenceable,
FfiRefFlag::Function,
])
});
const LIB_REF_FLAGS: FfiRefFlagList = FfiRefFlagList::new(
FfiRefFlag::Offsetable.value()
| FfiRefFlag::Readable.value()
| FfiRefFlag::Dereferenceable.value()
| FfiRefFlag::Function.value(),
);
pub struct FfiLib(Library);
@ -50,11 +47,8 @@ impl FfiLib {
.map_err(|err| LuaError::external(format!("{err}")))?
};
let luasym = lua.create_userdata(FfiRef::new(
(*sym).cast(),
(*LIB_REF_FLAGS).clone(),
UNSIZED_BOUNDS,
))?;
let luasym =
lua.create_userdata(FfiRef::new((*sym).cast(), LIB_REF_FLAGS, UNSIZED_BOUNDS))?;
set_association(lua, SYM_INNER, &luasym, &this)?;

View file

@ -7,8 +7,9 @@ use super::super::{FfiBox, FfiRef};
pub trait NativeDataHandle {
fn check_boundary(&self, offset: isize, size: usize) -> bool;
fn check_readable(&self, userdata: &LuaAnyUserData, offset: isize, size: usize) -> bool;
fn checek_writable(&self, userdata: &LuaAnyUserData, offset: isize, size: usize) -> bool;
fn check_readable(&self, offset: isize, size: usize) -> bool;
fn checek_writable(&self, offset: isize, size: usize) -> bool;
fn mark_ref(&self, userdata: &LuaAnyUserData, offset: isize, ptr: usize) -> LuaResult<()>;
unsafe fn get_pointer(&self, offset: isize) -> *mut ();
}

View file

@ -25,12 +25,17 @@ impl FfiRefFlagList {
pub fn zero() -> Self {
Self(0)
}
pub fn new(flags: &[FfiRefFlag]) -> Self {
let mut value = 0;
for i in flags {
value |= i.value();
}
Self(value)
pub const fn new(flags: u8) -> Self {
Self(flags)
}
pub const fn all() -> Self {
Self(
FfiRefFlag::Dereferenceable.value()
| FfiRefFlag::Readable.value()
| FfiRefFlag::Writable.value()
| FfiRefFlag::Offsetable.value()
| FfiRefFlag::Function.value(),
)
}
fn set(&mut self, value: bool, mask: u8) {
if value {

View file

@ -26,6 +26,8 @@ pub struct FfiRef {
ptr: *mut (),
pub flags: FfiRefFlagList,
pub boundary: FfiRefBounds,
// Save lua ffibox pointer
pub inner: Option<*const dyn NativeDataHandle>,
}
impl FfiRef {
@ -34,6 +36,7 @@ impl FfiRef {
ptr,
flags,
boundary: range,
inner: None,
}
}
@ -125,12 +128,34 @@ impl NativeDataHandle for FfiRef {
fn check_boundary(&self, offset: isize, size: usize) -> bool {
self.boundary.check_sized(offset, size)
}
fn checek_writable(&self, userdata: &LuaAnyUserData, offset: isize, size: usize) -> bool {
self.flags.is_writable()
fn checek_writable(&self, offset: isize, size: usize) -> bool {
// If unreadable ref
if !self.flags.is_writable() {
return false;
}
// If ref have inner luabox
if let Some(inner) = self.inner {
return unsafe { inner.as_ref().unwrap().checek_writable(offset, size) };
}
true
}
// TODO: if ref points box , check box too
fn check_readable(&self, userdata: &LuaAnyUserData, offset: isize, size: usize) -> bool {
self.flags.is_readable()
fn check_readable(&self, offset: isize, size: usize) -> bool {
// If unreadable ref
if !self.flags.is_readable() {
return false;
}
// If ref have inner luabox
if let Some(inner) = self.inner {
return unsafe { inner.as_ref().unwrap().check_readable(offset, size) };
}
true
}
fn mark_ref(&self, userdata: &LuaAnyUserData, offset: isize, ptr: usize) -> LuaResult<()> {
Ok(())
}
unsafe fn get_pointer(&self, offset: isize) -> *mut () {
self.get_ptr().byte_offset(offset)