Skip to content

Commit 1ec0630

Browse files
committed
Prevent user serializing the key
1 parent 534466d commit 1ec0630

File tree

10 files changed

+131
-0
lines changed

10 files changed

+131
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Lock\Exception;
13+
14+
/**
15+
* UnserializableKeyException is thrown when the key contains state that can no
16+
* be serialized and the user try to serialize it.
17+
* ie. Connection with a database, flock, semaphore, ...
18+
*
19+
* @author Jérémy Derussé <[email protected]>
20+
*/
21+
class UnserializableKeyException extends \RuntimeException implements ExceptionInterface
22+
{
23+
}

src/Symfony/Component/Lock/Key.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Lock;
1313

14+
use Symfony\Component\Lock\Exception\UnserializableKeyException;
15+
1416
/**
1517
* Key is a container for the state of the locks in stores.
1618
*
@@ -21,6 +23,7 @@ final class Key
2123
private $resource;
2224
private $expiringTime;
2325
private $state = [];
26+
private $serializable = true;
2427

2528
public function __construct(string $resource)
2629
{
@@ -52,6 +55,11 @@ public function getState(string $stateKey)
5255
return $this->state[$stateKey];
5356
}
5457

58+
public function markUnserializable(): void
59+
{
60+
$this->serializable = false;
61+
}
62+
5563
public function resetLifetime()
5664
{
5765
$this->expiringTime = null;
@@ -83,4 +91,13 @@ public function isExpired(): bool
8391
{
8492
return null !== $this->expiringTime && $this->expiringTime <= microtime(true);
8593
}
94+
95+
public function __sleep(): array
96+
{
97+
if (!$this->serializable) {
98+
throw new UnserializableKeyException('The key can not be serialized.');
99+
}
100+
101+
return ['resource', 'expiringTime', 'state'];
102+
}
86103
}

src/Symfony/Component/Lock/Store/FlockStore.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ private function lock(Key $key, bool $read, bool $blocking)
125125
}
126126

127127
$key->setState(__CLASS__, [$read, $handle]);
128+
$key->markUnserializable();
128129
}
129130

130131
/**

src/Symfony/Component/Lock/Store/SemaphoreStore.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ private function lock(Key $key, bool $blocking)
7676
}
7777

7878
$key->setState(__CLASS__, $resource);
79+
$key->markUnserializable();
7980
}
8081

8182
/**

src/Symfony/Component/Lock/Store/ZookeeperStore.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public function save(Key $key)
6565
$token = $this->getUniqueToken($key);
6666

6767
$this->createNewLock($resource, $token);
68+
$key->markUnserializable();
6869

6970
$this->checkNotExpired($key);
7071
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Lock\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Lock\Exception\UnserializableKeyException;
16+
use Symfony\Component\Lock\Key;
17+
18+
/**
19+
* @author Jérémy Derussé <[email protected]>
20+
*/
21+
class KeyTest extends TestCase
22+
{
23+
public function testSerialize()
24+
{
25+
$key = new Key(__METHOD__);
26+
$key->reduceLifetime(1);
27+
$key->setState('foo', 'bar');
28+
29+
$copy = unserialize(serialize($key));
30+
$this->assertSame($key->getState('foo'), $copy->getState('foo'));
31+
$this->assertEqualsWithDelta($key->getRemainingLifetime(), $copy->getRemainingLifetime(), 0.001);
32+
}
33+
34+
public function testUnserialize()
35+
{
36+
$key = new Key(__METHOD__);
37+
$key->markUnserializable();
38+
39+
$this->expectException(UnserializableKeyException::class);
40+
serialize($key);
41+
}
42+
}

src/Symfony/Component/Lock/Tests/Store/FlockStoreTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class FlockStoreTest extends AbstractStoreTest
2222
{
2323
use BlockingStoreTestTrait;
2424
use SharedLockStoreTestTrait;
25+
use UnserializableTestTrait;
2526

2627
/**
2728
* {@inheritdoc}

src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class SemaphoreStoreTest extends AbstractStoreTest
2424
{
2525
use BlockingStoreTestTrait;
26+
use UnserializableTestTrait;
2627

2728
/**
2829
* {@inheritdoc}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Lock\Tests\Store;
13+
14+
use Symfony\Component\Lock\Exception\UnserializableKeyException;
15+
use Symfony\Component\Lock\Key;
16+
use Symfony\Component\Lock\PersistingStoreInterface;
17+
18+
/**
19+
* @author Jérémy Derussé <[email protected]>
20+
*/
21+
trait UnserializableTestTrait
22+
{
23+
/**
24+
* @see AbstractStoreTest::getStore()
25+
*
26+
* @return PersistingStoreInterface
27+
*/
28+
abstract protected function getStore();
29+
30+
public function testUnserializableKey()
31+
{
32+
$store = $this->getStore();
33+
34+
$key = new Key(uniqid(__METHOD__, true));
35+
36+
$store->save($key);
37+
$this->assertTrue($store->exists($key));
38+
39+
$this->expectException(UnserializableKeyException::class);
40+
serialize($key);
41+
}
42+
}

src/Symfony/Component/Lock/Tests/Store/ZookeeperStoreTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
*/
2424
class ZookeeperStoreTest extends AbstractStoreTest
2525
{
26+
use UnserializableTestTrait;
27+
2628
/**
2729
* @return ZookeeperStore
2830
*/

0 commit comments

Comments
 (0)