Skip to content

Commit 3331832

Browse files
authored
Add ext/random Exception hierarchy (#9220)
* Add Random\Random{Error,Exception} and Random\BrokenRandomEngineError * Throw BrokenRandomEngineError * Throw RandomException on seeding failure * Throw RandomException when CSPRNG fails * Remove unused include from ext/random/engine_combinedlcg.c * Remove unused include from ext/random/engine_secure.c * Remove unused include from ext/random/random.c * [ci skip] Add ext/random Exception hierarchy to NEWS * [ci skip] Add the change of Exception for random_(int|bytes) to UPGRADING
1 parent dc01fce commit 3331832

13 files changed

+116
-29
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ PHP NEWS
77
PcgOneseq128XslRr64::__construct()). (timwolla)
88
. Removed redundant RuntimeExceptions from Randomizer methods. The
99
exceptions thrown by the engines will be exposed directly. (timwolla)
10+
. Added extension specific Exceptions/Errors (RandomException, RandomError,
11+
BrokenRandomEngineError). (timwolla)
1012

1113
- Standard:
1214
. Fixed bug #65489 (glob() basedir check is inconsistent). (Jakub Zelenka)

UPGRADING

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ PHP 8.2 UPGRADE NOTES
204204
dba_fetch(string|array $key, $skip, $dba): string|false
205205
is still accepted, but it is recommended to use the new standard variant.
206206

207+
- Random
208+
. random_bytes() and random_int() now throw \Random\RandomException on CSPRNG failure.
209+
Previously a plain \Exception was thrown.
210+
207211
- SPL
208212
. The $iterator parameter of iterator_to_array() and iterator_count() is
209213
widened to iterable from Iterator, allowing arrays to be passed.

ext/random/engine_combinedlcg.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include "php.h"
2323
#include "php_random.h"
2424

25-
#include "ext/spl/spl_exceptions.h"
2625
#include "Zend/zend_exceptions.h"
2726

2827
/*

ext/random/engine_mt19937.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
#include "php.h"
3131
#include "php_random.h"
3232

33-
#include "ext/spl/spl_exceptions.h"
3433
#include "Zend/zend_exceptions.h"
3534

3635
/*
@@ -280,7 +279,7 @@ PHP_METHOD(Random_Engine_Mt19937, __construct)
280279
if (seed_is_null) {
281280
/* MT19937 has a very large state, uses CSPRNG for seeding only */
282281
if (php_random_bytes_throw(&seed, sizeof(zend_long)) == FAILURE) {
283-
zend_throw_exception(spl_ce_RuntimeException, "Failed to generate a random seed", 0);
282+
zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
284283
RETURN_THROWS();
285284
}
286285
}

ext/random/engine_pcgoneseq128xslrr64.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "php.h"
2424
#include "php_random.h"
2525

26-
#include "ext/spl/spl_exceptions.h"
2726
#include "Zend/zend_exceptions.h"
2827

2928
static inline void step(php_random_status_state_pcgoneseq128xslrr64 *s)
@@ -149,7 +148,7 @@ PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, __construct)
149148

150149
if (seed_is_null) {
151150
if (php_random_bytes_throw(&state->state, sizeof(php_random_uint128_t)) == FAILURE) {
152-
zend_throw_exception(spl_ce_RuntimeException, "Failed to generate a random seed", 0);
151+
zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
153152
RETURN_THROWS();
154153
}
155154
} else {

ext/random/engine_secure.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include "php.h"
2323
#include "php_random.h"
2424

25-
#include "ext/spl/spl_exceptions.h"
2625
#include "Zend/zend_exceptions.h"
2726

2827
static uint64_t generate(php_random_status *status)

ext/random/engine_user.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ static uint64_t generate(php_random_status *status)
4949
result += ((uint64_t) (unsigned char) Z_STRVAL(retval)[i]) << (8 * i);
5050
}
5151
} else {
52-
zend_throw_error(NULL, "A random engine must return a non-empty string");
52+
zend_throw_error(random_ce_Random_BrokenRandomEngineError, "A random engine must return a non-empty string");
5353
return 0;
5454
}
5555

ext/random/engine_xoshiro256starstar.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#include "php.h"
2525
#include "php_random.h"
2626

27-
#include "ext/spl/spl_exceptions.h"
2827
#include "Zend/zend_exceptions.h"
2928

3029
static inline uint64_t splitmix64(uint64_t *seed)
@@ -207,7 +206,7 @@ PHP_METHOD(Random_Engine_Xoshiro256StarStar, __construct)
207206

208207
if (seed_is_null) {
209208
if (php_random_bytes_throw(&state->state, 32) == FAILURE) {
210-
zend_throw_exception(spl_ce_RuntimeException, "Failed to generate a random seed", 0);
209+
zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
211210
RETURN_THROWS();
212211
}
213212
} else {

ext/random/php_random.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,10 @@ typedef struct _php_random_randomizer {
260260
extern PHPAPI zend_class_entry *random_ce_Random_Engine;
261261
extern PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine;
262262

263+
extern PHPAPI zend_class_entry *random_ce_Random_RandomError;
264+
extern PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
265+
extern PHPAPI zend_class_entry *random_ce_Random_RandomException;
266+
263267
extern PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
264268
extern PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
265269
extern PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;

ext/random/random.c

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
#include "php.h"
2828

29-
#include "ext/spl/spl_exceptions.h"
3029
#include "Zend/zend_exceptions.h"
3130

3231
#include "php_random.h"
@@ -74,8 +73,13 @@ PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
7473
PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
7574
PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
7675
PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
76+
7777
PHPAPI zend_class_entry *random_ce_Random_Randomizer;
7878

79+
PHPAPI zend_class_entry *random_ce_Random_RandomError;
80+
PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
81+
PHPAPI zend_class_entry *random_ce_Random_RandomException;
82+
7983
static zend_object_handlers random_engine_mt19937_object_handlers;
8084
static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers;
8185
static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
@@ -121,7 +125,7 @@ static inline uint32_t rand_range32(const php_random_algo *algo, php_random_stat
121125
while (UNEXPECTED(result > limit)) {
122126
/* If the requirements cannot be met in a cycles, return fail */
123127
if (++count > RANDOM_RANGE_ATTEMPTS) {
124-
zend_throw_error(NULL, "Failed to generate an acceptable random number in %d attempts", RANDOM_RANGE_ATTEMPTS);
128+
zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", RANDOM_RANGE_ATTEMPTS);
125129
return 0;
126130
}
127131

@@ -177,7 +181,7 @@ static inline uint64_t rand_range64(const php_random_algo *algo, php_random_stat
177181
while (UNEXPECTED(result > limit)) {
178182
/* If the requirements cannot be met in a cycles, return fail */
179183
if (++count > RANDOM_RANGE_ATTEMPTS) {
180-
zend_throw_error(NULL, "Failed to generate an acceptable random number in %d attempts", RANDOM_RANGE_ATTEMPTS);
184+
zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", RANDOM_RANGE_ATTEMPTS);
181185
return 0;
182186
}
183187

@@ -470,7 +474,7 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw)
470474
/* Defer to CryptGenRandom on Windows */
471475
if (php_win32_get_random_bytes(bytes, size) == FAILURE) {
472476
if (should_throw) {
473-
zend_throw_exception(zend_ce_exception, "Failed to retrieve randomness from the operating system (BCryptGenRandom)", 0);
477+
zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (BCryptGenRandom)", 0);
474478
}
475479
return FAILURE;
476480
}
@@ -483,7 +487,7 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw)
483487
*/
484488
if (CCRandomGenerateBytes(bytes, size) != kCCSuccess) {
485489
if (should_throw) {
486-
zend_throw_exception(zend_ce_exception, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)", 0);
490+
zend_throw_exception(random_ce_Random_RandomException, "Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)", 0);
487491
}
488492
return FAILURE;
489493
}
@@ -548,9 +552,9 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw)
548552
if (fd < 0) {
549553
if (should_throw) {
550554
if (errno != 0) {
551-
zend_throw_exception_ex(zend_ce_exception, 0, "Cannot open /dev/urandom: %s", strerror(errno));
555+
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom: %s", strerror(errno));
552556
} else {
553-
zend_throw_exception_ex(zend_ce_exception, 0, "Cannot open /dev/urandom");
557+
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Cannot open /dev/urandom");
554558
}
555559
}
556560
return FAILURE;
@@ -568,9 +572,9 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw)
568572
close(fd);
569573
if (should_throw) {
570574
if (errno != 0) {
571-
zend_throw_exception_ex(zend_ce_exception, 0, "Error reading from /dev/urandom: %s", strerror(errno));
575+
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom: %s", strerror(errno));
572576
} else {
573-
zend_throw_exception_ex(zend_ce_exception, 0, "Error reading from /dev/urandom");
577+
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Error reading from /dev/urandom");
574578
}
575579
}
576580
return FAILURE;
@@ -589,9 +593,9 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw)
589593
if (read_bytes < size) {
590594
if (should_throw) {
591595
if (errno != 0) {
592-
zend_throw_exception_ex(zend_ce_exception, 0, "Could not gather sufficient random data: %s", strerror(errno));
596+
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data: %s", strerror(errno));
593597
} else {
594-
zend_throw_exception_ex(zend_ce_exception, 0, "Could not gather sufficient random data");
598+
zend_throw_exception_ex(random_ce_Random_RandomException, 0, "Could not gather sufficient random data");
595599
}
596600
}
597601
return FAILURE;
@@ -832,6 +836,15 @@ PHP_MINIT_FUNCTION(random)
832836
/* Random\CryptoSafeEngine */
833837
random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine);
834838

839+
/* Random\RandomError */
840+
random_ce_Random_RandomError = register_class_Random_RandomError(zend_ce_error);
841+
842+
/* Random\BrokenRandomEngineError */
843+
random_ce_Random_BrokenRandomEngineError = register_class_Random_BrokenRandomEngineError(random_ce_Random_RandomError);
844+
845+
/* Random\RandomException */
846+
random_ce_Random_RandomException = register_class_Random_RandomException(zend_ce_exception);
847+
835848
/* Random\Engine\Mt19937 */
836849
random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine);
837850
random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new;

ext/random/random.stub.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,25 @@ public function __serialize(): array {}
147147

148148
public function __unserialize(array $data): void {}
149149
}
150+
151+
/**
152+
* @strict-properties
153+
*/
154+
class RandomError extends \Error
155+
{
156+
}
157+
158+
/**
159+
* @strict-properties
160+
*/
161+
class BrokenRandomEngineError extends RandomError
162+
{
163+
}
164+
165+
/**
166+
* @strict-properties
167+
*/
168+
class RandomException extends \Exception
169+
{
170+
}
150171
}

ext/random/random_arginfo.h

Lines changed: 49 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/random/tests/03_randomizer/user_unsafe.phpt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,35 +74,35 @@ foreach ([
7474
EmptyStringEngine
7575
=====================
7676

77-
Error: A random engine must return a non-empty string in %s:%d
77+
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
7878
Stack trace:
7979
#0 %s(%d): Random\Randomizer->getInt(0, 123)
8080
#1 {main}
8181

8282
-------
8383

84-
Error: A random engine must return a non-empty string in %s:%d
84+
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
8585
Stack trace:
8686
#0 %s(%d): Random\Randomizer->nextInt()
8787
#1 {main}
8888

8989
-------
9090

91-
Error: A random engine must return a non-empty string in %s:%d
91+
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
9292
Stack trace:
9393
#0 %s(%d): Random\Randomizer->getBytes(1)
9494
#1 {main}
9595

9696
-------
9797

98-
Error: A random engine must return a non-empty string in %s:%d
98+
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
9999
Stack trace:
100100
#0 %s(%d): Random\Randomizer->shuffleArray(Array)
101101
#1 {main}
102102

103103
-------
104104

105-
Error: A random engine must return a non-empty string in %s:%d
105+
Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d
106106
Stack trace:
107107
#0 %s(%d): Random\Randomizer->shuffleBytes('foobar')
108108
#1 {main}
@@ -111,7 +111,7 @@ Stack trace:
111111
HeavilyBiasedEngine
112112
=====================
113113

114-
Error: Failed to generate an acceptable random number in 50 attempts in %s:%d
114+
Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d
115115
Stack trace:
116116
#0 %s(%d): Random\Randomizer->getInt(0, 123)
117117
#1 {main}
@@ -126,14 +126,14 @@ string(2) "ff"
126126

127127
-------
128128

129-
Error: Failed to generate an acceptable random number in 50 attempts in %s:%d
129+
Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d
130130
Stack trace:
131131
#0 %s(%d): Random\Randomizer->shuffleArray(Array)
132132
#1 {main}
133133

134134
-------
135135

136-
Error: Failed to generate an acceptable random number in 50 attempts in %s:%d
136+
Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d
137137
Stack trace:
138138
#0 %s(%d): Random\Randomizer->shuffleBytes('foobar')
139139
#1 {main}

0 commit comments

Comments
 (0)