// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::check_unstable; use crate::FfiPermissions; use deno_core::error::range_error; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::OpState; use std::ffi::c_char; use std::ffi::c_void; use std::ffi::CStr; use std::ptr; #[op2(fast)] pub fn op_ffi_ptr_create( state: &mut OpState, #[bigint] ptr_number: usize, ) -> Result<*mut c_void, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#create"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; Ok(ptr_number as *mut c_void) } #[op2(fast)] pub fn op_ffi_ptr_equals( state: &mut OpState, a: *const c_void, b: *const c_void, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#equals"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; Ok(a == b) } #[op2(fast)] pub fn op_ffi_ptr_of( state: &mut OpState, #[anybuffer] buf: *const u8, ) -> Result<*mut c_void, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#of"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; Ok(buf as *mut c_void) } #[op2(fast)] pub fn op_ffi_ptr_of_exact( state: &mut OpState, buf: v8::Local, ) -> Result<*mut c_void, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#of"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; let Some(buf) = buf.get_backing_store() else { return Ok(0 as _); }; let Some(buf) = buf.data() else { return Ok(0 as _); }; Ok(buf.as_ptr() as _) } #[op2(fast)] pub fn op_ffi_ptr_offset( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result<*mut c_void, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#offset"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid pointer to offset, pointer is null")); } // TODO(mmastrac): Create a RawPointer that can safely do pointer math. // SAFETY: Using `ptr.offset` is *actually unsafe* and has generated UB, but our FFI code relies on this working so we're going to // try and ask the compiler to be less undefined here by using `ptr.wrapping_offset`. Ok(ptr.wrapping_offset(offset)) } unsafe extern "C" fn noop_deleter_callback( _data: *mut c_void, _byte_length: usize, _deleter_data: *mut c_void, ) { } #[op2(fast)] pub fn op_ffi_ptr_value( state: &mut OpState, ptr: *mut c_void, #[buffer] out: &mut [u32], ) -> Result<(), AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#value"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; let outptr = out.as_ptr() as *mut usize; let length = out.len(); assert!( length >= (std::mem::size_of::() / std::mem::size_of::()) ); assert_eq!(outptr as usize % std::mem::size_of::(), 0); // SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned. let out = unsafe { &mut *outptr }; *out = ptr as usize; Ok(()) } #[op2] pub fn op_ffi_get_buf( scope: &mut v8::HandleScope<'scope>, state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, #[number] len: usize, ) -> Result, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getArrayBuffer"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid ArrayBuffer pointer, pointer is null")); } // SAFETY: Trust the user to have provided a real pointer, offset, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion. let backing_store = unsafe { v8::ArrayBuffer::new_backing_store_from_ptr( ptr.offset(offset), len, noop_deleter_callback, std::ptr::null_mut(), ) } .make_shared(); let array_buffer = v8::ArrayBuffer::with_backing_store(scope, &backing_store); Ok(array_buffer) } #[op2(fast)] pub fn op_ffi_buf_copy_into( state: &mut OpState, src: *mut c_void, #[number] offset: isize, #[anybuffer] dst: &mut [u8], #[number] len: usize, ) -> Result<(), AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#copyInto"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if src.is_null() { Err(type_error("Invalid ArrayBuffer pointer, pointer is null")) } else if dst.len() < len { Err(range_error( "Destination length is smaller than source length", )) } else { let src = src as *const c_void; // SAFETY: src and offset are user defined. // dest is properly aligned and is valid for writes of len * size_of::() bytes. unsafe { ptr::copy::(src.offset(offset) as *const u8, dst.as_mut_ptr(), len) }; Ok(()) } } #[op2] pub fn op_ffi_cstr_read( scope: &mut v8::HandleScope<'scope>, state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getCString"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid CString pointer, pointer is null")); } let cstr = // SAFETY: Pointer and offset are user provided. unsafe { CStr::from_ptr(ptr.offset(offset) as *const c_char) }.to_bytes(); let value = v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal) .ok_or_else(|| { type_error("Invalid CString pointer, string exceeds max length") })?; Ok(value) } #[op2(fast)] pub fn op_ffi_read_bool( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getBool"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid bool pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const bool) }) } #[op2(fast)] pub fn op_ffi_read_u8( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getUint8"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid u8 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const u8) as u32 }) } #[op2(fast)] pub fn op_ffi_read_i8( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getInt8"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid i8 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const i8) as i32 }) } #[op2(fast)] pub fn op_ffi_read_u16( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getUint16"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid u16 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const u16) as u32 }) } #[op2(fast)] pub fn op_ffi_read_i16( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getInt16"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid i16 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const i16) as i32 }) } #[op2(fast)] pub fn op_ffi_read_u32( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getUint32"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid u32 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const u32) }) } #[op2(fast)] pub fn op_ffi_read_i32( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getInt32"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid i32 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const i32) }) } #[op2(fast)] pub fn op_ffi_read_u64( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, #[buffer] out: &mut [u32], ) -> Result<(), AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getBigUint64"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; let outptr = out.as_mut_ptr() as *mut u64; assert!( out.len() >= (std::mem::size_of::() / std::mem::size_of::()) ); assert_eq!((outptr as usize % std::mem::size_of::()), 0); if ptr.is_null() { return Err(type_error("Invalid u64 pointer, pointer is null")); } let value = // SAFETY: ptr and offset are user provided. unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const u64) }; // SAFETY: Length and alignment of out slice were asserted to be correct. unsafe { *outptr = value }; Ok(()) } #[op2(fast)] pub fn op_ffi_read_i64( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, #[buffer] out: &mut [u32], ) -> Result<(), AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getBigUint64"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; let outptr = out.as_mut_ptr() as *mut i64; assert!( out.len() >= (std::mem::size_of::() / std::mem::size_of::()) ); assert_eq!((outptr as usize % std::mem::size_of::()), 0); if ptr.is_null() { return Err(type_error("Invalid i64 pointer, pointer is null")); } let value = // SAFETY: ptr and offset are user provided. unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const i64) }; // SAFETY: Length and alignment of out slice were asserted to be correct. unsafe { *outptr = value }; Ok(()) } #[op2(fast)] pub fn op_ffi_read_f32( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getFloat32"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid f32 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const f32) }) } #[op2(fast)] pub fn op_ffi_read_f64( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getFloat64"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid f64 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const f64) }) } #[op2(fast)] pub fn op_ffi_read_ptr( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, ) -> Result<*mut c_void, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointerView#getPointer"); let permissions = state.borrow_mut::(); permissions.check_partial(None)?; if ptr.is_null() { return Err(type_error("Invalid pointer pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<*mut c_void>(ptr.offset(offset) as *const *mut c_void) }) }