Skip to content

Commit e9098a5

Browse files
authored
PHPC-2219: Prohibit serializing PackedArray as root documents (#1480)
* PHPC-2219: Prohibit serializing PackedArray as root documents This adds logic to php_phongo_zval_to_bson_internal() to prohibit serializing PackedArray instances as a root document. Since this function is also used in specific cases to encode a BSON array, a PHONGO_BSON_ALLOW_ROOT_ARRAY flag is introduced to relax the restriction. * Check if existing field_path element must be freed before overwriting * Remove function name from Javascript code strings
1 parent a204130 commit e9098a5

File tree

51 files changed

+616
-141
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+616
-141
lines changed

src/BSON/Document.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ static PHP_METHOD(MongoDB_BSON_Document, fromPHP)
160160
intern = Z_DOCUMENT_OBJ_P(&zv);
161161

162162
intern->bson = bson_new();
163-
php_phongo_zval_to_bson(data, PHONGO_BSON_NONE, intern->bson, NULL);
163+
164+
// Explicitly allow constructing a Document from a PackedArray
165+
php_phongo_zval_to_bson(data, PHONGO_BSON_ALLOW_ROOT_ARRAY, intern->bson, NULL);
164166

165167
RETURN_ZVAL(&zv, 1, 1);
166168
}

src/MongoDB/BulkWrite.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ static bool php_phongo_bulkwrite_opts_append_array(bson_t* opts, const char* key
157157
return false;
158158
}
159159

160-
php_phongo_zval_to_bson(value, PHONGO_BSON_NONE, &b, NULL);
160+
// Explicitly allow MongoDB\BSON\PackedArray for array values
161+
php_phongo_zval_to_bson(value, PHONGO_BSON_ALLOW_ROOT_ARRAY, &b, NULL);
161162

162163
if (EG(exception)) {
163164
bson_destroy(&b);
@@ -441,7 +442,8 @@ static PHP_METHOD(MongoDB_Driver_BulkWrite, update)
441442
goto cleanup;
442443
}
443444

444-
php_phongo_zval_to_bson(zupdate, PHONGO_BSON_NONE, &bupdate, NULL);
445+
// Explicitly allow MongoDB\BSON\PackedArray for update pipelines
446+
php_phongo_zval_to_bson(zupdate, PHONGO_BSON_ALLOW_ROOT_ARRAY, &bupdate, NULL);
445447

446448
if (EG(exception)) {
447449
goto cleanup;

src/phongo_bson.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <Zend/zend_enum.h>
2222
#endif
2323
#include <Zend/zend_interfaces.h>
24+
#include <Zend/zend_portability.h>
2425

2526
#include "php_array_api.h"
2627

@@ -162,6 +163,13 @@ void php_phongo_field_path_write_item_at_current_level(php_phongo_field_path* fi
162163
php_phongo_field_path_ensure_allocation(field_path, field_path->size);
163164

164165
if (field_path->owns_elements) {
166+
/* Note: owns_elements is only used for field paths parsed from a type
167+
* map, so it's unlikely that an element would already have been
168+
* allocated here. This is in contrast to BSON encoding/decoding, which
169+
* frequently updates the field path and does not own elements. */
170+
if (UNEXPECTED(field_path->elements[field_path->size])) {
171+
efree(field_path->elements[field_path->size]);
172+
}
165173
field_path->elements[field_path->size] = estrdup(element);
166174
} else {
167175
field_path->elements[field_path->size] = (char*) element;

src/phongo_bson_encode.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,15 @@ static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path*
466466
}
467467

468468
if (instanceof_function(Z_OBJCE_P(data), php_phongo_packedarray_ce)) {
469+
/* If we are at the root-level, PackedArray instances should be
470+
* prohibited unless PHONGO_BSON_ALLOW_ROOT_ARRAY is set. */
471+
bool is_root_level = (field_path->size == 0);
472+
473+
if (is_root_level && !(flags & PHONGO_BSON_ALLOW_ROOT_ARRAY)) {
474+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "%s cannot be serialized as a root document", ZSTR_VAL(Z_OBJCE_P(data)->name));
475+
return;
476+
}
477+
469478
php_phongo_packedarray_t* intern = Z_PACKEDARRAY_OBJ_P(data);
470479

471480
phongo_bson_copy_to_noinit(intern->bson, bson);

src/phongo_bson_encode.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
#include <php.h>
2323

2424
typedef enum {
25-
PHONGO_BSON_NONE = 0x00,
26-
PHONGO_BSON_ADD_ID = 0x01,
27-
PHONGO_BSON_RETURN_ID = 0x02
25+
PHONGO_BSON_NONE = 0,
26+
PHONGO_BSON_ADD_ID = (1 << 0),
27+
PHONGO_BSON_RETURN_ID = (1 << 1),
28+
PHONGO_BSON_ALLOW_ROOT_ARRAY = (1 << 2)
2829
} php_phongo_bson_flags_t;
2930

3031
void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t* bson, bson_t** bson_out);

tests/bson/bson-fromPHP-001.phpt

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ $tests = array(
3434
array('foo' => 'bar'),
3535
(object) array(1, 2, 3),
3636
(object) array('foo' => 'bar'),
37-
# The PackedArray check will fail for instances of Persistable
37+
/* PackedArray cannot be serialized as a root document. Additionally, it
38+
* will fail return type validation for Persistable::bsonSerialize(). */
3839
MongoDB\BSON\PackedArray::fromPHP([1, 2, 3]),
3940
MongoDB\BSON\Document::fromPHP(['foo' => 'bar']),
4041
);
@@ -44,20 +45,28 @@ echo "Testing top-level objects\n";
4445
foreach ($tests as $test) {
4546
try {
4647
echo toJson(fromPHP(new MyDocument($test))), "\n";
48+
} catch (Exception $e) {
49+
printf("%s: %s\n", get_class($e), $e->getMessage());
50+
}
51+
try {
4752
echo toJson(fromPHP(new MyPersistableDocument($test))), "\n";
48-
} catch (MongoDB\Driver\Exception\UnexpectedValueException $e) {
49-
echo $e->getMessage(), "\n";
53+
} catch (Exception $e) {
54+
printf("%s: %s\n", get_class($e), $e->getMessage());
5055
}
5156
}
5257

5358
echo "\nTesting nested objects\n";
5459

5560
foreach ($tests as $test) {
5661
try {
57-
echo toJson(fromPHP(new MyDocument(array('nested' => new MyDocument($test))))), "\n";
58-
echo toJson(fromPHP(new MyDocument(array('nested' => new MyPersistableDocument($test))))), "\n";
59-
} catch (MongoDB\Driver\Exception\UnexpectedValueException $e) {
60-
echo $e->getMessage(), "\n";
62+
echo toJson(fromPHP(new MyDocument(['nested' => new MyDocument($test)]))), "\n";
63+
} catch (Exception $e) {
64+
printf("%s: %s\n", get_class($e), $e->getMessage());
65+
}
66+
try {
67+
echo toJson(fromPHP(new MyDocument(['nested' => new MyPersistableDocument($test)]))), "\n";
68+
} catch (Exception $e) {
69+
printf("%s: %s\n", get_class($e), $e->getMessage());
6170
}
6271
}
6372

@@ -74,8 +83,8 @@ Testing top-level objects
7483
{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 }
7584
{ "foo" : "bar" }
7685
{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" }
77-
{ "0" : 1, "1" : 2, "2" : 3 }
78-
Expected MyPersistableDocument::bsonSerialize() to return an array, stdClass, or MongoDB\BSON\Document, MongoDB\BSON\PackedArray given
86+
MongoDB\Driver\Exception\UnexpectedValueException: MongoDB\BSON\PackedArray cannot be serialized as a root document
87+
MongoDB\Driver\Exception\UnexpectedValueException: Expected MyPersistableDocument::bsonSerialize() to return an array, stdClass, or MongoDB\BSON\Document, MongoDB\BSON\PackedArray given
7988
{ "foo" : "bar" }
8089
{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" }
8190

@@ -89,7 +98,7 @@ Testing nested objects
8998
{ "nested" : { "foo" : "bar" } }
9099
{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } }
91100
{ "nested" : [ 1, 2, 3 ] }
92-
Expected MyPersistableDocument::bsonSerialize() to return an array, stdClass, or MongoDB\BSON\Document, MongoDB\BSON\PackedArray given
101+
MongoDB\Driver\Exception\UnexpectedValueException: Expected MyPersistableDocument::bsonSerialize() to return an array, stdClass, or MongoDB\BSON\Document, MongoDB\BSON\PackedArray given
93102
{ "nested" : { "foo" : "bar" } }
94103
{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } }
95104
===DONE===

tests/bson/bson-fromPHP_error-003.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class UnknownType implements MongoDB\BSON\Type {}
1010
$tests = array(
1111
new UnknownType,
1212
new MongoDB\BSON\Binary('foobar', MongoDB\BSON\Binary::TYPE_GENERIC),
13-
new MongoDB\BSON\Javascript('function foo(bar) {var baz = bar; var bar = foo; return bar; }'),
13+
new MongoDB\BSON\Javascript('function(bar) {var baz = bar; var bar = foo; return bar; }'),
1414
new MongoDB\BSON\MinKey,
1515
new MongoDB\BSON\MaxKey,
1616
new MongoDB\BSON\ObjectId,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
MongoDB\BSON\fromPHP(): PackedArray cannot be serialized as root document
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/basic.inc';
7+
8+
echo throws(function() {
9+
fromPHP(MongoDB\BSON\PackedArray::fromPHP([]));
10+
}, MongoDB\Driver\Exception\UnexpectedValueException::class), "\n";
11+
12+
?>
13+
===DONE===
14+
<?php exit(0); ?>
15+
--EXPECT--
16+
OK: Got MongoDB\Driver\Exception\UnexpectedValueException
17+
MongoDB\BSON\PackedArray cannot be serialized as a root document
18+
===DONE===

tests/bson/bson-javascript-001.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ MongoDB\BSON\Javascript #001
55

66
require_once __DIR__ . '/../utils/basic.inc';
77

8-
$js = new MongoDB\BSON\Javascript("function foo(bar) {var baz = bar; var bar = foo; return bar; }");
9-
$jswscope = new MongoDB\BSON\Javascript("function foo(bar) {var baz = bar; var bar = foo; return bar; }", array("foo" => 42));
8+
$js = new MongoDB\BSON\Javascript("function(bar) {var baz = bar; var bar = foo; return bar; }");
9+
$jswscope = new MongoDB\BSON\Javascript("function(bar) {var baz = bar; var bar = foo; return bar; }", array("foo" => 42));
1010
$tests = array(
1111
array("js" => $js),
1212
array("js" => $jswscope),

tests/bson/bson-javascript-002.phpt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ MongoDB\BSON\Javascript debug handler
55

66
$tests = array(
77
array(
8-
'function foo(bar) { return bar; }',
8+
'function(bar) { return bar; }',
99
array(),
1010
),
1111
array(
12-
'function foo() { return foo; }',
12+
'function() { return foo; }',
1313
array('foo' => 42),
1414
),
1515
array(
16-
'function foo() { return id; }',
16+
'function() { return id; }',
1717
array('id' => new MongoDB\BSON\ObjectId('53e2a1c40640fd72175d4603')),
1818
),
1919
);
@@ -31,14 +31,14 @@ foreach ($tests as $test) {
3131
--EXPECTF--
3232
object(MongoDB\BSON\Javascript)#%d (%d) {
3333
["code"]=>
34-
string(33) "function foo(bar) { return bar; }"
34+
string(29) "function(bar) { return bar; }"
3535
["scope"]=>
3636
object(stdClass)#%d (%d) {
3737
}
3838
}
3939
object(MongoDB\BSON\Javascript)#%d (%d) {
4040
["code"]=>
41-
string(30) "function foo() { return foo; }"
41+
string(26) "function() { return foo; }"
4242
["scope"]=>
4343
object(stdClass)#%d (%d) {
4444
["foo"]=>
@@ -47,7 +47,7 @@ object(MongoDB\BSON\Javascript)#%d (%d) {
4747
}
4848
object(MongoDB\BSON\Javascript)#%d (%d) {
4949
["code"]=>
50-
string(29) "function foo() { return id; }"
50+
string(25) "function() { return id; }"
5151
["scope"]=>
5252
object(stdClass)#%d (%d) {
5353
["id"]=>

0 commit comments

Comments
 (0)