Skip to content

Commit 1297538

Browse files
committed
PHPC-848: Consult ZEND_HASH_APPLY_PROTECTION() in PHP7
When a numeric array was encoded to BSON, the hash's apply counter was incremented and decremented irrespective of whether apply protection was enabled. This was not compatible with immutable arrays, which may be created by OPcache. We now consult ZEND_HASH_APPLY_PROTECTION() whenever an apply counter might be modified. Previously, object_to_bson() incremented the apply counter when converting a MongoDB\BSON\Type instance. Associative arrays and other objects were not protected. This patch adds recursion checking for those types. Since PHP7 already has macros for working with the apply counter, this patch adds equivalent macros for PHP 5.x.
1 parent bf64016 commit 1297538

File tree

3 files changed

+66
-15
lines changed

3 files changed

+66
-15
lines changed

phongo_compat.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,10 @@
167167
# define Z_ISUNDEF(x) !x
168168
# define phongo_free_object_arg void
169169
# define phongo_zpp_char_len int
170-
# define ZEND_HASH_APPLY_COUNT(ht) (ht)->nApplyCount
170+
# define ZEND_HASH_APPLY_PROTECTION(ht) true
171+
# define ZEND_HASH_GET_APPLY_COUNT(ht) ((ht)->nApplyCount)
172+
# define ZEND_HASH_DEC_APPLY_COUNT(ht) ((ht)->nApplyCount -= 1)
173+
# define ZEND_HASH_INC_APPLY_COUNT(ht) ((ht)->nApplyCount += 1)
171174
# define PHONGO_RETVAL_STRINGL(s, slen) RETVAL_STRINGL(s, slen, 1)
172175
# define PHONGO_RETURN_STRINGL(s, slen) RETURN_STRINGL(s, slen, 1)
173176
# define PHONGO_RETURN_STRING(s) RETURN_STRING(s, 1)

src/bson.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -862,8 +862,8 @@ void object_to_bson(zval *object, php_phongo_bson_flags_t flags, const char *key
862862
tmp_ht = HASH_OF(obj_data);
863863
#endif
864864

865-
if (tmp_ht) {
866-
ZEND_HASH_APPLY_COUNT(tmp_ht)++;
865+
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
866+
ZEND_HASH_INC_APPLY_COUNT(tmp_ht);
867867
}
868868

869869
/* Persistable objects must always be serialized as BSON documents;
@@ -897,8 +897,8 @@ void object_to_bson(zval *object, php_phongo_bson_flags_t flags, const char *key
897897
bson_append_array_end(bson, &child);
898898
}
899899

900-
if (tmp_ht) {
901-
ZEND_HASH_APPLY_COUNT(tmp_ht)--;
900+
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
901+
ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
902902
}
903903
zval_ptr_dtor(&obj_data);
904904
return;
@@ -970,12 +970,22 @@ void object_to_bson(zval *object, php_phongo_bson_flags_t flags, const char *key
970970
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Unexpected %s instance: %s", php_phongo_type_ce->name, Z_OBJCE_P(object)->name);
971971
#endif
972972
return;
973-
}
973+
} else {
974+
HashTable *tmp_ht = HASH_OF(object);
975+
976+
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
977+
ZEND_HASH_INC_APPLY_COUNT(tmp_ht);
978+
}
974979

975-
mongoc_log(MONGOC_LOG_LEVEL_TRACE, MONGOC_LOG_DOMAIN, "encoding document");
976-
bson_append_document_begin(bson, key, key_len, &child);
977-
phongo_zval_to_bson(object, flags, &child, NULL TSRMLS_CC);
978-
bson_append_document_end(bson, &child);
980+
mongoc_log(MONGOC_LOG_LEVEL_TRACE, MONGOC_LOG_DOMAIN, "encoding document");
981+
bson_append_document_begin(bson, key, key_len, &child);
982+
phongo_zval_to_bson(object, flags, &child, NULL TSRMLS_CC);
983+
bson_append_document_end(bson, &child);
984+
985+
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
986+
ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
987+
}
988+
}
979989
}
980990

981991
static void phongo_bson_append(bson_t *bson, php_phongo_bson_flags_t flags, const char *key, long key_len, zval *entry TSRMLS_DC)
@@ -1023,16 +1033,16 @@ static void phongo_bson_append(bson_t *bson, php_phongo_bson_flags_t flags, cons
10231033
bson_t child;
10241034
HashTable *tmp_ht = HASH_OF(entry);
10251035

1026-
if (tmp_ht) {
1027-
ZEND_HASH_APPLY_COUNT(tmp_ht)++;
1036+
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
1037+
ZEND_HASH_INC_APPLY_COUNT(tmp_ht);
10281038
}
10291039

10301040
bson_append_array_begin(bson, key, key_len, &child);
10311041
phongo_zval_to_bson(entry, flags, &child, NULL TSRMLS_CC);
10321042
bson_append_array_end(bson, &child);
10331043

1034-
if (tmp_ht) {
1035-
ZEND_HASH_APPLY_COUNT(tmp_ht)--;
1044+
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
1045+
ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
10361046
}
10371047
break;
10381048
}
@@ -1191,7 +1201,7 @@ PHONGO_API void phongo_zval_to_bson(zval *data, php_phongo_bson_flags_t flags, b
11911201
return;
11921202
}
11931203

1194-
if (!ht_data || ZEND_HASH_APPLY_COUNT(ht_data) > 1) {
1204+
if (!ht_data || ZEND_HASH_GET_APPLY_COUNT(ht_data) > 1) {
11951205
#if PHP_VERSION_ID >= 70000
11961206
if (Z_TYPE_P(data) == IS_OBJECT && instanceof_function(Z_OBJCE_P(data), php_phongo_serializable_ce TSRMLS_CC)) {
11971207
#endif

tests/bson/bson-fromPHP-004.phpt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
BSON\fromPHP(): PHP documents with circular references
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
echo "\nTesting packed array with circular reference\n";
9+
10+
$document = ['x' => 1, 'y' => []];
11+
$document['y'][] = &$document['y'];
12+
echo toJson(fromPHP($document)), "\n";
13+
14+
echo "\nTesting associative array with circular reference\n";
15+
16+
$document = ['x' => 1, 'y' => []];
17+
$document['y']['z'] = &$document['y'];
18+
echo toJson(fromPHP($document)), "\n";
19+
20+
echo "\nTesting object with circular reference\n";
21+
22+
$document = (object) ['x' => 1, 'y' => (object) []];
23+
$document->y->z = &$document->y;
24+
echo toJson(fromPHP($document)), "\n";
25+
26+
?>
27+
===DONE===
28+
<?php exit(0); ?>
29+
--EXPECTF--
30+
Testing packed array with circular reference
31+
{ "x" : 1, "y" : [ [ ] ] }
32+
33+
Testing associative array with circular reference
34+
{ "x" : 1, "y" : { "z" : { } } }
35+
36+
Testing object with circular reference
37+
{ "x" : 1, "y" : { "z" : { } } }
38+
===DONE===

0 commit comments

Comments
 (0)