Skip to content

Commit 513d5c9

Browse files
committed
Allow full integer range from random_int()
1 parent 3d413ad commit 513d5c9

File tree

2 files changed

+32
-24
lines changed

2 files changed

+32
-24
lines changed

ext/standard/basic_functions.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1899,10 +1899,11 @@ ZEND_END_ARG_INFO()
18991899
/* }}} */
19001900
/* {{{ random.c */
19011901
ZEND_BEGIN_ARG_INFO_EX(arginfo_random_bytes, 0, 0, 0)
1902-
ZEND_ARG_INFO(0, bytes)
1902+
ZEND_ARG_INFO(0, length)
19031903
ZEND_END_ARG_INFO()
19041904

19051905
ZEND_BEGIN_ARG_INFO_EX(arginfo_random_int, 0, 0, 0)
1906+
ZEND_ARG_INFO(0, min)
19061907
ZEND_ARG_INFO(0, max)
19071908
ZEND_END_ARG_INFO()
19081909
/* }}} */

ext/standard/random.c

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -105,42 +105,49 @@ PHP_FUNCTION(random_bytes)
105105
Return an arbitrary pseudo-random integer */
106106
PHP_FUNCTION(random_int)
107107
{
108+
zend_long min = ZEND_LONG_MIN;
108109
zend_long max = ZEND_LONG_MAX;
109-
zend_long limit;
110-
zend_long result;
110+
zend_ulong limit;
111+
zend_ulong umax;
112+
zend_ulong result;
111113

112-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &max) == FAILURE) {
114+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &min, &max) == FAILURE) {
113115
return;
114116
}
115117

116-
if (max <= 0 || max > ZEND_LONG_MAX) {
117-
php_error_docref(NULL, E_WARNING, "Cannot use maximum less than 1 or greater than %d", ZEND_LONG_MAX);
118+
if (min >= max) {
119+
php_error_docref(NULL, E_WARNING, "Minimum value must be greater than the maximum value");
118120
RETURN_FALSE;
119121
}
120122

121-
// Special case so we can return a range inclusive of the upper bound
122-
if (max == ZEND_LONG_MAX) {
123-
if (php_random_bytes(&result, sizeof(result)) == FAILURE) {
124-
return;
125-
}
126-
RETURN_LONG(result & ZEND_LONG_MAX);
127-
}
123+
umax = max - min;
128124

129-
// Increment the max so the range is inclusive of max
130-
max++;
125+
if (php_random_bytes(&result, sizeof(result)) == FAILURE) {
126+
return;
127+
}
131128

132-
// Ceiling under which ZEND_LONG_MAX % max == 0
133-
limit = ZEND_LONG_MAX - (ZEND_LONG_MAX % max) - 1;
129+
// Special case where no modulus is required
130+
if (umax == ZEND_ULONG_MAX) {
131+
RETURN_LONG((zend_long)result);
132+
}
134133

135-
// Discard numbers over the limit to avoid modulo bias
136-
do {
137-
if (php_random_bytes(&result, sizeof(result)) == FAILURE) {
138-
return;
134+
// Increment the max so the range is inclusive of max
135+
umax++;
136+
137+
// Powers of two are not biased
138+
if (umax & ~umax != umax) {
139+
// Ceiling under which ZEND_LONG_MAX % max == 0
140+
limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1;
141+
142+
// Discard numbers over the limit to avoid modulo bias
143+
while (result > limit) {
144+
if (php_random_bytes(&result, sizeof(result)) == FAILURE) {
145+
return;
146+
}
139147
}
140-
result &= ZEND_LONG_MAX;
141-
} while (result > limit);
148+
}
142149

143-
RETURN_LONG(result % max);
150+
RETURN_LONG((zend_long)((result % umax) + min));
144151
}
145152
/* }}} */
146153

0 commit comments

Comments
 (0)