Skip to content

Commit fea6da2

Browse files
committed
Fix possible collisions in ObjectId::create() when used inside a forked subprocess
1 parent 475c30a commit fea6da2

File tree

3 files changed

+17
-7
lines changed

3 files changed

+17
-7
lines changed

ChangeLog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ MongoDB for XP Framework ChangeLog
33

44
## ?.?.? / ????-??-??
55

6+
## 2.4.1 / 2024-11-27
7+
8+
* Fixed possible collisions in `ObjectId::create()` when used within a
9+
forked subprocess by including the process ID in the random value's
10+
calculation.
11+
(@thekid)
12+
613
## 2.4.0 / 2024-10-14
714

815
* Merged PR #48: Extend error handling to include if a write was retried

src/main/php/com/mongodb/ObjectId.class.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
use lang\{Value, IllegalArgumentException};
44

5+
/** @test com.mongodb.unittest.ObjectIdTest */
56
class ObjectId implements Value {
6-
private static $rand, $counter;
7+
private static $counter;
8+
private static $rand= [];
79
private $string;
810

911
static function __static() {
10-
self::$rand= random_bytes(5);
1112
self::$counter= random_int(0, 4294967294); // uint32
1213
}
1314

@@ -29,7 +30,7 @@ public function __construct($string) {
2930
* Creates a new random object ID consisting of:
3031
*
3132
* - a 4-byte timestamp value (uses current time if omitted)
32-
* - a 5-byte random value
33+
* - a 5-byte random value (per process)
3334
* - a 3-byte incrementing counter, initialized to a random value
3435
*
3536
* @param ?int $timestamp
@@ -38,10 +39,11 @@ public function __construct($string) {
3839
*/
3940
public static function create($timestamp= null): self {
4041
$uint32= self::$counter > 4294967294 ? self::$counter= 0 : ++self::$counter;
42+
$pid= getmypid();
4143
return new self(bin2hex(pack(
4244
'Na5aaa',
4345
null === $timestamp ? time() : $timestamp,
44-
self::$rand,
46+
self::$rand[$pid] ?? self::$rand[$pid]= random_bytes(5),
4547
chr($uint32 >> 16),
4648
chr($uint32 >> 8),
4749
chr($uint32)

src/test/php/com/mongodb/unittest/ObjectIdTest.class.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ public function create() {
4949
}
5050

5151
#[Test]
52-
public function create_generates_unique_ids() {
53-
Assert::notEquals(ObjectId::create(), ObjectId::create());
52+
public function create_with_same_timestamp_generates_unique_ids() {
53+
$ts= time();
54+
Assert::notEquals(ObjectId::create($ts), ObjectId::create($ts));
5455
}
5556

5657
#[Test]
57-
public function create_from_timestamp() {
58+
public function create_from_timestamp_is_monotonic() {
5859
$t= time();
5960
$oid= ObjectId::create($t);
6061
Assert::equals(dechex($t), substr($oid, 0, 8));

0 commit comments

Comments
 (0)