Prevent segfaults on orphaned subview objects.

This commit is contained in:
CismonX 2019-09-23 01:22:08 +08:00
parent aeba61a787
commit 7faf5d6fd6
18 changed files with 148 additions and 60 deletions

View File

@ -181,8 +181,9 @@ namespace php_arma
fci.retval = &retval;
auto native = THIS_NATIVE;
native->for_each([&fci, &fcc](auto&& val) {
ZVAL_OBJ(&fci.params[0], mapval_dense<T>::create(&val));
auto parent = get_parent_zval<ChildT>(&EX(This));
native->for_each([&fci, &fcc, parent](auto&& val) {
ZVAL_OBJ(&fci.params[0], mapval_dense<T>::create(parent, &val));
zend_call_function(&fci, &fcc);
zval_ptr_dtor(&fci.params[0]);
zval_ptr_dtor(fci.retval);

View File

@ -10,11 +10,11 @@ namespace php_arma
{
template <typename T, const char *Name>
zend_always_inline
void property_declare(zend_class_entry *ce)
void property_declare_scalar(zend_class_entry *ce)
{
zval property;
zval_set_scalar(&property, static_cast<T>(0));
zend_declare_property(ce, Name, str::get_len(Name), &property, ZEND_ACC_PUBLIC);
property_declare<Name, ZEND_ACC_PUBLIC>(ce, &property);
}
template <typename T>
@ -393,8 +393,9 @@ namespace php_arma
void complex<T>::ce_init(zend_class_entry *parent_ce)
{
ce = class_register<php_name::val>(parent_ce, me);
property_declare<T, real_property_name>(ce);
property_declare<T, imag_property_name>(ce);
property_declare_scalar<T, real_property_name>(ce);
property_declare_scalar<T, imag_property_name>(ce);
object_handlers_init(&handlers);
handlers.read_dimension = read_dimension;
handlers.write_property = write_property;

View File

@ -14,8 +14,8 @@
namespace php_arma
{
template <typename T, typename T1>
PHP_ARMA_METHOD(dense_matrix, __invoke, T, T1)
template <typename T, typename ChildT>
PHP_ARMA_METHOD(dense_matrix, __invoke, T, ChildT)
{
zend_long i, j;
ZEND_PARSE_PARAMETERS_START(1, 2)
@ -25,13 +25,14 @@ namespace php_arma
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
auto parent = get_parent_zval<ChildT>(&EX(This));
zend_object *zobj;
try {
if (EX_NUM_ARGS() == 1) {
zobj = mapval_dense<T>::create(&native->operator()(i));
zobj = mapval_dense<T>::create(parent, &native->operator()(i));
} else {
zobj = mapval_dense<T>::create(&native->operator()(i, j));
zobj = mapval_dense<T>::create(parent, &native->operator()(i, j));
}
} catch (const std::logic_error& err) {
// Index out of bounds.
@ -42,8 +43,8 @@ namespace php_arma
RETVAL_OBJ(zobj);
}
template <typename T, typename T1>
PHP_ARMA_METHOD(dense_matrix, at, T, T1)
template <typename T, typename ChildT>
PHP_ARMA_METHOD(dense_matrix, at, T, ChildT)
{
zend_long i, j;
ZEND_PARSE_PARAMETERS_START(1, 2)
@ -53,12 +54,13 @@ namespace php_arma
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
auto parent = get_parent_zval<ChildT>(&EX(This));
zend_object *zobj;
if (EX_NUM_ARGS() == 1) {
zobj = mapval_dense<T>::create(&native->operator[](i));
zobj = mapval_dense<T>::create(parent, &native->operator[](i));
} else {
zobj = mapval_dense<T>::create(&native->at(i, j));
zobj = mapval_dense<T>::create(parent, &native->at(i, j));
}
RETVAL_OBJ(zobj);
}
@ -87,7 +89,8 @@ namespace php_arma
using ret_t = typename ChildT::subview_t;
auto native = THIS_NATIVE;
auto zobj = ret_t::create(std::move(native->cols(first_col, last_col)));
auto parent = get_parent_zval<ChildT>(&EX(This));
auto zobj = ret_t::create(parent, std::move(native->cols(first_col, last_col)));
RETVAL_OBJ(zobj);
}
@ -103,7 +106,8 @@ namespace php_arma
using ret_t = typename ChildT::subview_t;
auto native = THIS_NATIVE;
auto zobj = ret_t::create(std::move(native->rows(first_row, last_row)));
auto parent = get_parent_zval<ChildT>(&EX(This));
auto zobj = ret_t::create(parent, std::move(native->rows(first_row, last_row)));
RETVAL_OBJ(zobj);
}
@ -121,12 +125,13 @@ namespace php_arma
using ret_t = typename ChildT::subview_t;
auto native = THIS_NATIVE;
auto zobj = ret_t::create(std::move(native->submat(first_row, first_col, last_row, last_col)));
auto parent = get_parent_zval<ChildT>(&EX(This));
auto zobj = ret_t::create(parent, std::move(native->submat(first_row, first_col, last_row, last_col)));
RETVAL_OBJ(zobj);
}
template <typename T, typename T1>
PHP_ARMA_METHOD(dense_matrix, diag, T, T1)
template <typename T, typename ChildT>
PHP_ARMA_METHOD(dense_matrix, diag, T, ChildT)
{
zend_long k = 0;
ZEND_PARSE_PARAMETERS_START(0, 1)
@ -135,10 +140,11 @@ namespace php_arma
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
auto parent = get_parent_zval<ChildT>(&EX(This));
zend_object *zobj;
try {
zobj = diagview<T>::create(std::move(native->diag(k)));
zobj = diagview<T>::create(parent, std::move(native->diag(k)));
} catch (const std::logic_error& err) {
// requested diagonal out of bounds
throw_exception(err.what());

View File

@ -175,7 +175,7 @@ namespace php_arma
using ret_t = typename ChildT::subview_t;
auto native = THIS_NATIVE;
auto zobj = ret_t::create(std::move(native->head_cols(n_cols)));
auto zobj = ret_t::create(&EX(This), std::move(native->head_cols(n_cols)));
RETVAL_OBJ(zobj);
}
@ -190,7 +190,7 @@ namespace php_arma
using ret_t = typename ChildT::subview_t;
auto native = THIS_NATIVE;
auto zobj = ret_t::create(std::move(native->head_rows(n_rows)));
auto zobj = ret_t::create(&EX(This), std::move(native->head_rows(n_rows)));
RETVAL_OBJ(zobj);
}
@ -205,7 +205,7 @@ namespace php_arma
using ret_t = typename ChildT::subview_t;
auto native = THIS_NATIVE;
auto zobj = ret_t::create(std::move(native->tail_cols(n_cols)));
auto zobj = ret_t::create(&EX(This), std::move(native->tail_cols(n_cols)));
RETVAL_OBJ(zobj);
}
@ -220,7 +220,7 @@ namespace php_arma
using ret_t = typename ChildT::subview_t;
auto native = THIS_NATIVE;
auto zobj = ret_t::create(std::move(native->tail_rows(n_rows)));
auto zobj = ret_t::create(&EX(This), std::move(native->tail_rows(n_rows)));
RETVAL_OBJ(zobj);
}

View File

@ -32,6 +32,7 @@ namespace php_arma
PHP_ARMA_FENTRY(me)
));
ce->create_object = object_non_constructible;
declare_subview_parent(ce);
object_handlers_init(&handlers);
handlers.offset = sizeof(native_t);

View File

@ -8,16 +8,16 @@
#define PHP_ARMA_DIAGONAL_HH
#include "php_arma.hh"
#include "instantiable.hh"
#include "complex.hh"
#include "shared_methods.hh"
namespace php_arma
{
template <typename T> struct mat;
template <typename T, typename T1> struct linear;
template <typename T, bool IsSparse>
struct diagonal
struct diagonal : instantiable<diagonal<T, IsSparse>, true>
{
using native_t = std::conditional_t<IsSparse, arma::spdiagview<T>, arma::diagview<T>>;
using orig_t = mat<T>;
@ -26,13 +26,6 @@ namespace php_arma
friend void diagonal_init();
template <typename... Ts>
zend_always_inline
static zend_object *create(Ts&&... args)
{
return object_create_ctor<native_t>(ce, &handlers, args...);
}
PHP_ARMA_CE_HANDLRES_DECLARE();
private:

41
src/instantiable.hh Normal file
View File

@ -0,0 +1,41 @@
//
// php-armadillo/instantiable.hh
//
// @Author CismonX
//
#ifndef PHP_ARMA_INSTANTIABLE_HH
#define PHP_ARMA_INSTANTIABLE_HH
#include "php_arma.hh"
namespace php_arma
{
template <typename ChildT, bool IsSubview> struct instantiable;
template <typename ChildT>
struct instantiable<ChildT, false>
{
template <typename... Ts>
zend_always_inline
static zend_object *create(Ts&&... args)
{
return object_create_ctor<typename ChildT::native_t>(ChildT::ce, &ChildT::handlers, args...);
}
};
template <typename ChildT>
struct instantiable<ChildT, true>
{
template <typename... Ts>
zend_always_inline
static zend_object *create(zval *parent, Ts&&... args)
{
auto zobj = object_create_ctor<typename ChildT::native_t>(ChildT::ce, &ChildT::handlers, args...);
object_set_property(zobj, 0, parent);
return zobj;
}
};
}
#endif // !PHP_ARMA_INSTANTIABLE_HH

View File

@ -44,6 +44,8 @@ namespace php_arma
{
ce = class_register<php_name::val>(parent_ce, me);
ce->create_object = object_non_constructible;
declare_subview_parent(ce);
object_handlers_init(&handlers);
handlers.offset = sizeof(native_t);
handlers.clone_obj = nullptr;

View File

@ -56,19 +56,22 @@ namespace php_arma
}
zend_always_inline
static zend_object *create(native_t init_val)
static zend_object *create(zval *parent, native_t init_val)
{
zend_object *zobj;
if constexpr (IsSparse) {
return object_create<native_t>(ce, [&init_val](auto obj) {
zobj = object_create<native_t>(ce, [&init_val](auto obj) {
memcpy(obj, &init_val, sizeof(native_t));
return &handlers;
});
} else {
return object_create<native_t>(ce, [init_val](auto obj) {
zobj = object_create<native_t>(ce, [init_val](auto obj) {
*obj = init_val;
return &handlers;
});
}
object_set_property(zobj, 0, parent);
return zobj;
}
PHP_ARMA_CE_HANDLRES_DECLARE();

View File

@ -29,7 +29,7 @@ namespace php_arma
Z_PARAM_LONG(fill)
ZEND_PARSE_PARAMETERS_END();
auto zobj = create(n_rows, n_cols);
auto zobj = mat::create(n_rows, n_cols);
fill::invoke(ZOBJ_NATIVE(zobj), fill);
RETVAL_OBJ(zobj);
}
@ -42,7 +42,7 @@ namespace php_arma
Z_PARAM_STR(text)
ZEND_PARSE_PARAMETERS_END();
auto zobj = create(ZSTR_VAL(text));
auto zobj = mat::create(ZSTR_VAL(text));
RETVAL_OBJ(zobj);
}
@ -56,7 +56,7 @@ namespace php_arma
auto num_rows = zend_hash_num_elements(Z_ARR_P(arr));
if (UNEXPECTED(num_rows == 0)) {
RETURN_OBJ(create());
RETURN_OBJ(mat::create());
}
auto first_row = zend_hash_index_find(Z_ARR_P(arr), 0);
if (UNEXPECTED(first_row == nullptr || Z_TYPE_P(first_row) != IS_ARRAY)) {
@ -65,10 +65,10 @@ namespace php_arma
}
auto num_cols = zend_hash_num_elements(Z_ARR_P(first_row));
if (UNEXPECTED(num_rows == 0)) {
RETURN_OBJ(create());
RETURN_OBJ(mat::create());
}
auto zobj = create(num_rows, num_cols);
auto zobj = mat::create(num_rows, num_cols);
auto native = ZOBJ_NATIVE(zobj);
size_t idx_row = 0;
size_t idx_col = 0;

View File

@ -8,6 +8,7 @@
#define PHP_ARMA_MAT_HH
#include "php_arma.hh"
#include "instantiable.hh"
#include "complex.hh"
namespace php_arma
@ -15,7 +16,7 @@ namespace php_arma
template <typename T> struct subview_mat;
template <typename T>
struct mat
struct mat : instantiable<mat<T>, false>
{
using native_t = arma::Mat<T>;
using orig_t = mat<T>;
@ -25,13 +26,6 @@ namespace php_arma
friend void mat_init();
template <typename... Ts>
zend_always_inline
static zend_object *create(Ts&&... args)
{
return object_create_ctor<native_t>(ce, &handlers, args...);
}
PHP_ARMA_CE_HANDLRES_DECLARE();
private:

View File

@ -180,6 +180,7 @@ namespace php_arma
constexpr const char namespace_prefix[] = "Arma\\";
constexpr const char internal_prefix[] = "Internal\\";
constexpr const char empty[] = "";
template <const char *Str>
constexpr auto with_arma_prefix
@ -334,6 +335,19 @@ namespace php_arma
return ce;
}
template <const char *Name, int AccessType>
zend_always_inline
void property_declare(zend_class_entry *ce, zval *zv)
{
zend_declare_property(ce, Name, str::get_len(Name), zv, AccessType);
}
zend_always_inline
void declare_subview_parent(zend_class_entry *ce)
{
property_declare<str::empty, ZEND_ACC_PRIVATE>(ce, &EG(uninitialized_zval));
}
zend_always_inline
void object_handlers_init(zend_object_handlers *handlers)
{
@ -354,6 +368,17 @@ namespace php_arma
return reinterpret_cast<T*>(zobj) - 1;
}
template <typename T>
zend_always_inline
zval *get_parent_zval(zval *current)
{
if constexpr (std::is_same_v<T, typename T::orig_t>) {
return current;
} else {
return OBJ_PROP_NUM(Z_OBJ_P(current), 0);
}
}
/// Helper functions for function entry.
zend_always_inline
@ -398,10 +423,12 @@ namespace php_arma
zend_always_inline
zend_object *object_create(zend_class_entry *ce, F&& init)
{
auto obj = reinterpret_cast<T*>(emalloc(sizeof(T) + sizeof(zend_object) - sizeof(zval)));
auto obj = reinterpret_cast<T*>(emalloc(sizeof(T) +
sizeof(zend_object) + zend_object_properties_size(ce)));
auto zobj = to_zend_object(obj);
zobj->handlers = init(obj);
zend_object_std_init(zobj, ce);
object_properties_init(zobj, ce);
return zobj;
}

View File

@ -19,10 +19,11 @@ namespace php_arma
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
auto parent = get_parent_zval<ChildT>(&EX(This));
zend_object *zobj;
try {
zobj = mapval_dense<T>::create(&native->operator()(i));
zobj = mapval_dense<T>::create(parent, &native->operator()(i));
} catch (const std::logic_error& err) {
// Index out of bounds.
throw_exception(err.what());
@ -41,8 +42,9 @@ namespace php_arma
ZEND_PARSE_PARAMETERS_END();
auto native = THIS_NATIVE;
auto parent = get_parent_zval<ChildT>(&EX(This));
auto zobj = mapval_dense<T>::create(&native->operator[](i));
auto zobj = mapval_dense<T>::create(parent, &native->operator[](i));
RETVAL_OBJ(zobj);
}

View File

@ -34,6 +34,7 @@ namespace php_arma
PHP_ARMA_FENTRY(me)
));
ce->create_object = object_non_constructible;
declare_subview_parent(ce);
object_handlers_init(&handlers);
handlers.offset = sizeof(native_t);

View File

@ -8,6 +8,7 @@
#define PHP_ARMA_SUBVIEW_MAT_HH
#include "php_arma.hh"
#include "instantiable.hh"
#include "complex.hh"
namespace php_arma
@ -15,7 +16,7 @@ namespace php_arma
template <typename T> struct mat;
template <typename T>
struct subview_mat
struct subview_mat : instantiable<subview_mat<T>, true>
{
using native_t = arma::subview<T>;
using orig_t = mat<T>;
@ -25,13 +26,6 @@ namespace php_arma
friend void subview_mat_init();
template <typename... Ts>
zend_always_inline
static zend_object *create(Ts&&... args)
{
return object_create_ctor<native_t>(ce, &handlers, args...);
}
PHP_ARMA_CE_HANDLRES_DECLARE();
private:

View File

@ -18,5 +18,15 @@ batch_assert('read/write access of `MapVal`',
[7, $mat(0, 1)->val()]
);
// When parent class is destroyed...
$orphaned_mapval = Arma\IMat::fromString('1 2; 3 4')->at(1, 1);
batch_assert('orphaned `MapVal`',
[$orphaned_mapval->val(), 4]
);
$orphaned_mapval->setTo(7);
batch_assert('orphaned `MapVal`',
[$orphaned_mapval->val(), 7]
);
?>
--EXPECT--

View File

@ -27,5 +27,13 @@ batch_assert('`SvMat`',
[PHP_INT_MAX, $mat->at(1, 2)->val()]
);
// When parent class is destroyed...
$orphaned_submat = Arma\IMat::fromString('1 2; 3 4')->cols(0, 0);
batch_assert('orphaned `SvMat`',
[$orphaned_submat->at(0, 0)->val(), 1],
[$orphaned_submat->at(1, 0)->val(), 3]
);
?>
--EXPECT--

View File

@ -18,9 +18,13 @@ $diag->rawPrint();
$diag->at(1)->setTo($diag->at(0)->val());
$mat->rawPrint();
// When parent class is destroyed...
Arma\IMat::fromString("1 2; 3 4")->diag(-1)->rawPrint();
?>
--EXPECT--
1
4
1 2
3 1
3