Skip to content

Commit dcf92fa

Browse files
fixed memory corruption issue when making calls from an extension back to user space php functions
1 parent 1012391 commit dcf92fa

File tree

3 files changed

+130
-22
lines changed

3 files changed

+130
-22
lines changed

include/value.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* this class.
1616
*
1717
* @author Emiel Bruijntjes <[email protected]>
18-
* @copyright 2013 - 2019 Copernica BV
18+
* @copyright 2013 - 2024 Copernica BV
1919
*/
2020

2121
/**
@@ -1133,7 +1133,7 @@ class PHPCPP_EXPORT Value : private HashParent
11331133
* @param argv The parameters
11341134
* @return Value
11351135
*/
1136-
Value exec(int argc, Value *argv) const;
1136+
Value exec(int argc, Value argv[]) const;
11371137

11381138
/**
11391139
* Call method with a number of parameters
@@ -1142,8 +1142,8 @@ class PHPCPP_EXPORT Value : private HashParent
11421142
* @param argv The parameters
11431143
* @return Value
11441144
*/
1145-
Value exec(const char *name, int argc, Value *argv) const;
1146-
Value exec(const char *name, int argc, Value *argv);
1145+
Value exec(const char *name, int argc, Value argv[]) const;
1146+
Value exec(const char *name, int argc, Value argv[]);
11471147

11481148
/**
11491149
* Refcount - the number of references to the value
@@ -1242,6 +1242,7 @@ class PHPCPP_EXPORT Value : private HashParent
12421242
friend class Script;
12431243
friend class ConstantImpl;
12441244
friend class Stream;
1245+
friend class ExecArguments;
12451246

12461247
/**
12471248
* Friend functions which have to access that zval directly

zend/execarguments.h

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* ExecArguments.h
3+
*
4+
* Helper class that we use to turn an array of Value objects into an
5+
* array of zval parameters
6+
*
7+
* @author Emiel Bruijntjes <[email protected]>
8+
* @copyright 2024 Copernica BV
9+
*/
10+
11+
/**
12+
* Include guard
13+
*/
14+
#pragma once
15+
16+
/**
17+
* Begin of namespace
18+
*/
19+
namespace Php {
20+
21+
/**
22+
* Class definition
23+
*/
24+
class ExecArguments
25+
{
26+
private:
27+
/**
28+
* Short-array-optimization (most exec calls do not have more than 10 parameters)
29+
* @var zval[]
30+
*/
31+
zval _preallocated[10];
32+
33+
/**
34+
* The actual arguments
35+
* @var zval[]
36+
*/
37+
zval *_argv;
38+
39+
/**
40+
* The number of arguments
41+
* @var size_t
42+
*/
43+
size_t _argc;
44+
45+
public:
46+
/**
47+
* Default constructor
48+
*/
49+
ExecArguments() : _argv(_preallocated), _argc(0) {}
50+
51+
/**
52+
* Constructor
53+
* @param argc
54+
* @param argv
55+
*/
56+
ExecArguments(size_t argc, Value argv[]) : _argv(_preallocated), _argc(argc)
57+
{
58+
// if there are too many arguments, we allocate them right away
59+
if (_argc > 10) _argv = (zval *)malloc(sizeof(zval) * _argc);
60+
61+
// convert Value objects to zval array with references
62+
for (size_t i = 0; i < argc; ++i)
63+
{
64+
// make sure the original variable is a reference so that our copy points to the same data
65+
// @todo not sure if this is needed, do we need to turn the parameters into references to allow for pass-by-reference parameters?
66+
//if (!Z_ISREF_P(argv[i]._val)) ZVAL_MAKE_REF(argv[i]._val);
67+
68+
// copy the zval
69+
ZVAL_COPY(&_argv[i], argv[i]._val);
70+
}
71+
}
72+
73+
/**
74+
* No copying (we could implement this later, but for now this is not needed)
75+
* @param that
76+
*/
77+
ExecArguments(const ExecArguments &that) = delete;
78+
79+
/**
80+
* Destructor
81+
*/
82+
virtual ~ExecArguments()
83+
{
84+
// destruct all zval objects
85+
for (size_t i = 0; i < _argc; ++i) zval_ptr_dtor(&_argv[i]);
86+
87+
// deallocate memory
88+
if (_argv != _preallocated) free(_argv);
89+
}
90+
91+
/**
92+
* Convert to a argv[]
93+
* @return zval[]
94+
*/
95+
zval *argv() { return _argv; }
96+
int argc() { return _argc; }
97+
};
98+
99+
/**
100+
* End of namespace
101+
*/
102+
}

zend/value.cpp

+23-18
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
*
2323
*
2424
* @author Emiel Bruijntjes <[email protected]>
25-
* @copyright 2013 - 2019 Copernica BV
25+
* @copyright 2019 - 2024 Copernica BV
2626
*/
2727
#include "includes.h"
2828
#include "string.h"
2929
#include "lowercase.h"
3030
#include "macros.h"
31+
#include "execarguments.h"
3132

3233
/**
3334
* Set up namespace
@@ -822,6 +823,19 @@ static Value do_exec(const zval *object, zval *method, int argc, zval *argv)
822823
}
823824
}
824825

826+
/**
827+
* Helper function that runs the actual call
828+
* @param object The object to call it on
829+
* @param method The function or method to call
830+
* @param args The parameters
831+
* @return Value
832+
*/
833+
static Value do_exec(const zval *object, zval *method, ExecArguments &args)
834+
{
835+
// pass on
836+
return do_exec(object, method, args.argc(), args.argv());
837+
}
838+
825839
/**
826840
* Call the function in PHP
827841
* We have ten variants of this function, depending on the number of parameters
@@ -919,16 +933,13 @@ Value Value::call(const char *name)
919933
* @param argv The parameters
920934
* @return Value
921935
*/
922-
Value Value::exec(int argc, Value *argv) const
936+
Value Value::exec(int argc, Value argv[]) const
923937
{
924938
// array of zvals to execute
925-
zval* params = static_cast<zval*>(alloca(argc * sizeof(zval)));
926-
927-
// convert all the values
928-
for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; }
929-
939+
ExecArguments args(argc, argv);
940+
930941
// call helper function
931-
return do_exec(nullptr, _val, argc, params);
942+
return do_exec(nullptr, _val, args);
932943
}
933944

934945
/**
@@ -944,13 +955,10 @@ Value Value::exec(const char *name, int argc, Value *argv) const
944955
Value method(name);
945956

946957
// array of zvals to execute
947-
zval* params = static_cast<zval*>(alloca(argc * sizeof(zval)));
948-
949-
// convert all the values
950-
for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; }
958+
ExecArguments args(argc, argv);
951959

952960
// call helper function
953-
return do_exec(_val, method._val, argc, params);
961+
return do_exec(_val, method._val, args);
954962
}
955963

956964
/**
@@ -966,13 +974,10 @@ Value Value::exec(const char *name, int argc, Value *argv)
966974
Value method(name);
967975

968976
// array of zvals to execute
969-
zval* params = static_cast<zval*>(alloca(argc * sizeof(zval)));
970-
971-
// convert all the values
972-
for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; }
977+
ExecArguments args(argc, argv);
973978

974979
// call helper function
975-
return do_exec(_val, method._val, argc, params);
980+
return do_exec(_val, method._val, args);
976981
}
977982

978983
/**

0 commit comments

Comments
 (0)