Skip to content

Commit a2b71a6

Browse files
committed
PHPC-329: Apply more validation to type map class
Per the serialization spec, we should check that the class (1) exists, (2), is instantiatable, and (3) implements Unserializable. Additionally, this refactors some code duplication out of apply_classname_to_state(). We should also always check to free classname, since an empty string might have been allocated (i.e. classname_len is zero).
1 parent 802e2ae commit a2b71a6

File tree

1 file changed

+44
-48
lines changed

1 file changed

+44
-48
lines changed

src/bson.c

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <ext/spl/spl_array.h>
3131
#include <Zend/zend_hash.h>
3232
#include <Zend/zend_interfaces.h>
33+
#include <Zend/zend_string.h>
3334

3435
/* PHP array helpers */
3536
#include "src/contrib/php_array_api.h"
@@ -62,6 +63,9 @@
6263

6364
#define PHONGO_ODM_FIELD_NAME "__pclass"
6465

66+
#define PHONGO_IS_CLASS_INSTANTIATABLE(ce) \
67+
(!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)))
68+
6569
PHP_MINIT_FUNCTION(bson)
6670
{
6771
(void)type; /* We don't care if we are loaded via dl() or extension= */
@@ -964,71 +968,63 @@ PHP_FUNCTION(fromPHP)
964968
}
965969
/* }}} */
966970

971+
static void apply_classname_to_state(const char *classname, int classname_len, php_phongo_bson_typemap_types *type, zend_class_entry **type_ce TSRMLS_DC)
972+
{
973+
if (!strcasecmp(classname, "array")) {
974+
*type = PHONGO_TYPEMAP_NATIVE_ARRAY;
975+
*type_ce = NULL;
976+
} else if (!strcasecmp(classname, "stdclass") || !strcasecmp(classname, "object")) {
977+
*type = PHONGO_TYPEMAP_NATIVE_OBJECT;
978+
*type_ce = NULL;
979+
} else {
980+
zend_class_entry *found_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO|ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
981+
982+
if (!found_ce) {
983+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Class %s does not exist", classname);
984+
} else if (!PHONGO_IS_CLASS_INSTANTIATABLE(found_ce)) {
985+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Class %s is not instantiatable", classname);
986+
} else if (!instanceof_function(found_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
987+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Class %s does not implement %s\\Unserializable", classname, BSON_NAMESPACE);
988+
} else {
989+
*type = PHONGO_TYPEMAP_CLASS;
990+
*type_ce = found_ce;
991+
}
992+
}
993+
}
994+
967995
void php_phongo_bson_typemap_to_state(zval *typemap, php_phongo_bson_typemap *map TSRMLS_DC)
968996
{
969997
if (typemap) {
970-
char *classname;
971-
int classname_len;
972-
zend_bool classname_free = 0;
998+
char *classname;
999+
int classname_len;
1000+
zend_bool classname_free = 0;
9731001

9741002
classname = php_array_fetchl_string(typemap, "array", sizeof("array")-1, &classname_len, &classname_free);
9751003
if (classname_len) {
976-
if (!strcasecmp(classname, "array")) {
977-
map->array_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
978-
} else if (!strcasecmp(classname, "stdclass") || !strcasecmp(classname, "object")) {
979-
map->array_type = PHONGO_TYPEMAP_NATIVE_OBJECT;
980-
} else {
981-
zend_class_entry *array_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
982-
map->array_type = PHONGO_TYPEMAP_CLASS;
983-
984-
if (instanceof_function(array_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
985-
map->array = array_ce;
986-
}
987-
}
988-
if (classname_free) {
989-
efree(classname);
990-
}
1004+
apply_classname_to_state(classname, classname_len, &map->array_type, &map->array TSRMLS_CC);
1005+
}
1006+
if (classname_free) {
1007+
str_efree(classname);
9911008
}
9921009

9931010
classname = php_array_fetchl_string(typemap, "document", sizeof("document")-1, &classname_len, &classname_free);
9941011
if (classname_len) {
995-
if (!strcasecmp(classname, "array")) {
996-
map->document_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
997-
} else if (!strcasecmp(classname, "stdclass") || !strcasecmp(classname, "object")) {
998-
map->document_type = PHONGO_TYPEMAP_NATIVE_OBJECT;
999-
} else {
1000-
zend_class_entry *document_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1001-
map->document_type = PHONGO_TYPEMAP_CLASS;
1002-
1003-
if (instanceof_function(document_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
1004-
map->document = document_ce;
1005-
}
1006-
}
1007-
if (classname_free) {
1008-
efree(classname);
1009-
}
1012+
apply_classname_to_state(classname, classname_len, &map->document_type, &map->document TSRMLS_CC);
1013+
}
1014+
if (classname_free) {
1015+
str_efree(classname);
10101016
}
10111017

10121018
classname = php_array_fetchl_string(typemap, "root", sizeof("root")-1, &classname_len, &classname_free);
10131019
if (classname_len) {
1014-
if (!strcasecmp(classname, "array")) {
1015-
map->root_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
1016-
} else if (!strcasecmp(classname, "stdclass") || !strcasecmp(classname, "object")) {
1017-
map->root_type = PHONGO_TYPEMAP_NATIVE_OBJECT;
1018-
} else {
1019-
zend_class_entry *root_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1020-
map->root_type = PHONGO_TYPEMAP_CLASS;
1021-
1022-
if (instanceof_function(root_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
1023-
map->root = root_ce;
1024-
}
1025-
}
1026-
if (classname_free) {
1027-
efree(classname);
1028-
}
1020+
apply_classname_to_state(classname, classname_len, &map->root_type, &map->root TSRMLS_CC);
1021+
}
1022+
if (classname_free) {
1023+
str_efree(classname);
10291024
}
10301025
}
10311026
}
1027+
10321028
/* {{{ proto array|object BSON\toPHP(string $bson [, array $typemap = array()])
10331029
Returns the PHP representation of a BSON value, optionally converting it into a custom class */
10341030
PHP_FUNCTION(toPHP)

0 commit comments

Comments
 (0)