From 47f1b8c7253de98dc9366b92d247c8ea3024cbbc Mon Sep 17 00:00:00 2001 From: David Cole Date: Thu, 9 Sep 2021 16:41:58 +1200 Subject: [PATCH 1/2] Fix alignment of class objects --- src/php/execution_data.rs | 6 ++-- src/php/types/object.rs | 65 +++++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/php/execution_data.rs b/src/php/execution_data.rs index 5b95b14ba7..a6f96bf252 100644 --- a/src/php/execution_data.rs +++ b/src/php/execution_data.rs @@ -30,10 +30,8 @@ impl ExecutionData { /// 1. Contains an object. /// 2. The object was originally derived from `T`. pub unsafe fn get_object(&self) -> Option> { - let ptr = self.This.object()? as *const ZendObject as *mut u8; - let offset = mem::size_of::(); - let ptr = ptr.offset(0 - offset as isize) as *mut ZendClassObject; - Some(ClassObject::from_zend_class_object(&mut *ptr, false)) + let ptr = ZendClassObject::::from_zend_obj_ptr(self.This.object()?)?; + Some(ClassObject::from_zend_class_object(ptr, false)) } /// Attempts to retrieve the 'this' object, which can be used in class methods diff --git a/src/php/types/object.rs b/src/php/types/object.rs index 2b7475b6ad..f0139b2d2c 100644 --- a/src/php/types/object.rs +++ b/src/php/types/object.rs @@ -2,13 +2,12 @@ //! allowing users to store Rust data inside a PHP object. use std::{ - alloc::Layout, convert::TryInto, fmt::Debug, marker::PhantomData, mem::{self, MaybeUninit}, ops::{Deref, DerefMut}, - ptr, + ptr::{self, NonNull}, sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; @@ -347,15 +346,9 @@ impl<'a, T: RegisteredClass> FromZval<'a> for &'a T { const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); fn from_zval(zval: &'a Zval) -> Option { - let obj = zval.object()?; + let cobj = ZendClassObject::::from_zend_obj_ptr(zval.object()?)?; - if obj.is_instance::() { - // SAFETY: If the zend object is an instance of `T`, we can guarantee that the memory before - // it is occupied by an instance of `T`. - unsafe { ((obj as *mut ZendObject) as *mut T).offset(-1).as_ref() } - } else { - None - } + Some(unsafe { &*cobj.obj.as_mut_ptr() }) } } @@ -363,15 +356,9 @@ impl<'a, T: RegisteredClass> FromZval<'a> for &'a mut T { const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME)); fn from_zval(zval: &'a Zval) -> Option { - let obj = zval.object()?; + let cobj = ZendClassObject::::from_zend_obj_ptr(zval.object()?)?; - if obj.is_instance::() { - // SAFETY: If the zend object is an instance of `T`, we can guarantee that the memory before - // it is occupied by an instance of `T`. - unsafe { ((obj as *mut ZendObject) as *mut T).offset(-1).as_mut() } - } else { - None - } + Some(unsafe { &mut *cobj.obj.as_mut_ptr() }) } } @@ -439,7 +426,7 @@ where /// Representation of a Zend class object in memory. Usually seen through its managed variant /// of [`ClassObject`]. #[repr(C)] -pub(crate) struct ZendClassObject { +pub(crate) struct ZendClassObject { obj: MaybeUninit, std: zend_object, } @@ -486,13 +473,46 @@ impl ZendClassObject { } } + /// Returns a reference to the [`ZendClassObject`] of a given zend object `obj`. Returns [`None`] + /// if the given object is not of the type `T`. + /// + /// # Parameters + /// + /// * `obj` - The zend object to get the [`ZendClassObject`] for. + pub(crate) fn from_zend_obj_ptr(obj: &zend_object) -> Option<&mut Self> { + let ptr = obj as *const zend_object as *const i8; + let ptr = unsafe { + let ptr = ptr.offset(0 - Self::std_offset() as isize) as *const Self; + (ptr as *mut Self).as_mut()? + }; + + if ptr.std.is_instance::() { + Some(ptr) + } else { + None + } + } + /// Returns a mutable reference to the underlying Zend object. pub(crate) fn get_mut_zend_obj(&mut self) -> &mut zend_object { &mut self.std } } -impl Drop for ZendClassObject { +impl ZendClassObject { + /// Returns the offset of the `std` property in the class object. + pub(crate) fn std_offset() -> usize { + unsafe { + let null = NonNull::::dangling(); + let base = null.as_ref() as *const Self; + let std = &null.as_ref().std as *const zend_object; + + (std as usize) - (base as usize) + } + } +} + +impl Drop for ZendClassObject { fn drop(&mut self) { // SAFETY: All constructors guarantee that `obj` is valid. unsafe { std::ptr::drop_in_place(self.obj.as_mut_ptr()) }; @@ -585,8 +605,7 @@ impl ZendObjectHandlers { /// Caller must guarantee that the `ptr` given is a valid memory location. pub unsafe fn init(ptr: *mut ZendObjectHandlers) { pub unsafe extern "C" fn free_obj(object: *mut zend_object) { - let layout = Layout::new::(); - let offset = layout.size(); + let offset = ZendClassObject::::std_offset(); // Cast to *mut u8 to work in byte offsets let ptr = (object as *mut u8).offset(0 - offset as isize) as *mut T; @@ -599,7 +618,7 @@ impl ZendObjectHandlers { } std::ptr::copy_nonoverlapping(&std_object_handlers, ptr, 1); - let offset = std::mem::size_of::(); + let offset = ZendClassObject::::std_offset(); (*ptr).offset = offset as _; (*ptr).free_obj = Some(free_obj::); } From 6fa8f20c91d86aaa57317564cd326339eab9aaab Mon Sep 17 00:00:00 2001 From: David Cole Date: Thu, 9 Sep 2021 16:44:25 +1200 Subject: [PATCH 2/2] Clippy --- src/php/execution_data.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/php/execution_data.rs b/src/php/execution_data.rs index a6f96bf252..a2d342fd40 100644 --- a/src/php/execution_data.rs +++ b/src/php/execution_data.rs @@ -1,8 +1,6 @@ //! Functions for interacting with the execution data passed to PHP functions\ //! introduced in Rust. -use std::mem; - use crate::{ bindings::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}, errors::{Error, Result},