Skip to content

Commit

Permalink
Fix alignment of class objects (#66)
Browse files Browse the repository at this point in the history
* Fix alignment of class objects

* Clippy
  • Loading branch information
davidcole1340 authored Sep 9, 2021
1 parent f506a41 commit dec4d8c
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 29 deletions.
8 changes: 2 additions & 6 deletions src/php/execution_data.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -30,10 +28,8 @@ impl ExecutionData {
/// 1. Contains an object.
/// 2. The object was originally derived from `T`.
pub unsafe fn get_object<T: RegisteredClass>(&self) -> Option<ClassObject<'static, T>> {
let ptr = self.This.object()? as *const ZendObject as *mut u8;
let offset = mem::size_of::<T>();
let ptr = ptr.offset(0 - offset as isize) as *mut ZendClassObject<T>;
Some(ClassObject::from_zend_class_object(&mut *ptr, false))
let ptr = ZendClassObject::<T>::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
Expand Down
65 changes: 42 additions & 23 deletions src/php/types/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand Down Expand Up @@ -347,31 +346,19 @@ 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<Self> {
let obj = zval.object()?;
let cobj = ZendClassObject::<T>::from_zend_obj_ptr(zval.object()?)?;

if obj.is_instance::<T>() {
// 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() })
}
}

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<Self> {
let obj = zval.object()?;
let cobj = ZendClassObject::<T>::from_zend_obj_ptr(zval.object()?)?;

if obj.is_instance::<T>() {
// 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() })
}
}

Expand Down Expand Up @@ -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<T: RegisteredClass> {
pub(crate) struct ZendClassObject<T> {
obj: MaybeUninit<T>,
std: zend_object,
}
Expand Down Expand Up @@ -486,13 +473,46 @@ impl<T: RegisteredClass> ZendClassObject<T> {
}
}

/// 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::<T>() {
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<T: RegisteredClass> Drop for ZendClassObject<T> {
impl<T> ZendClassObject<T> {
/// Returns the offset of the `std` property in the class object.
pub(crate) fn std_offset() -> usize {
unsafe {
let null = NonNull::<Self>::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<T> Drop for ZendClassObject<T> {
fn drop(&mut self) {
// SAFETY: All constructors guarantee that `obj` is valid.
unsafe { std::ptr::drop_in_place(self.obj.as_mut_ptr()) };
Expand Down Expand Up @@ -585,8 +605,7 @@ impl ZendObjectHandlers {
/// Caller must guarantee that the `ptr` given is a valid memory location.
pub unsafe fn init<T>(ptr: *mut ZendObjectHandlers) {
pub unsafe extern "C" fn free_obj<T>(object: *mut zend_object) {
let layout = Layout::new::<T>();
let offset = layout.size();
let offset = ZendClassObject::<T>::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;
Expand All @@ -599,7 +618,7 @@ impl ZendObjectHandlers {
}

std::ptr::copy_nonoverlapping(&std_object_handlers, ptr, 1);
let offset = std::mem::size_of::<T>();
let offset = ZendClassObject::<T>::std_offset();
(*ptr).offset = offset as _;
(*ptr).free_obj = Some(free_obj::<T>);
}
Expand Down

0 comments on commit dec4d8c

Please sign in to comment.