Skip to content

Commit

Permalink
Fix shift in rand_rangeXX() (#9088)
Browse files Browse the repository at this point in the history
The previous shifting logic is problematic for two reasons:

1. It invokes undefined behavior when the `->last_generated_size` is at least
as large as the target integer in `result`, because the shift is larger than
the target integer. This was reported in GH-9083.

2. It expands the returned bytes in a big-endian fashion: Earlier bytes are
shifting into the most-significant position. As all the other logic in the
random extension treats byte-strings as little-endian numbers this is
inconsistent.

By fixing the second issue, we can implicitly fix the first one: Instead of
shifting the existing bits by the number of "newly added" bits, we shift the
newly added bits by the number of existing bits. As we stop requesting new bits
once the total_size reached the size of the target integer we can be sure to
never invoke undefined behavior during shifting.

The get_int_user.phpt test was adjusted to verify the little-endian behavior.
It generates a single byte per call and we expect the first byte generated to
appear at the start of the resulting number.

see GH-9056 for a previous fix in the same area.
Fixes GH-9083 which reports the undefined behavior.
Resolves GH-9085 which was an alternative attempt to fix GH-9083.
  • Loading branch information
TimWolla committed Jul 22, 2022
1 parent c8f4801 commit ab5491f
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 6 deletions.
8 changes: 4 additions & 4 deletions ext/random/random.c
Expand Up @@ -92,7 +92,7 @@ static inline uint32_t rand_range32(const php_random_algo *algo, php_random_stat
total_size = 0;
do {
r = algo->generate(status);
result = (result << (8 * status->last_generated_size)) | r;
result = result | (r << (total_size * 8));
total_size += status->last_generated_size;
if (status->last_unsafe) {
return 0;
Expand Down Expand Up @@ -127,7 +127,7 @@ static inline uint32_t rand_range32(const php_random_algo *algo, php_random_stat
total_size = 0;
do {
r = algo->generate(status);
result = (result << (8 * status->last_generated_size)) | r;
result = result | (r << (total_size * 8));
total_size += status->last_generated_size;
if (status->last_unsafe) {
return 0;
Expand All @@ -148,7 +148,7 @@ static inline uint64_t rand_range64(const php_random_algo *algo, php_random_stat
total_size = 0;
do {
r = algo->generate(status);
result = (result << (8 * status->last_generated_size)) | r;
result = result | (r << (total_size * 8));
total_size += status->last_generated_size;
if (status->last_unsafe) {
return 0;
Expand Down Expand Up @@ -183,7 +183,7 @@ static inline uint64_t rand_range64(const php_random_algo *algo, php_random_stat
total_size = 0;
do {
r = algo->generate(status);
result = (result << (8 * status->last_generated_size)) | r;
result = result | (r << (total_size * 8));
total_size += status->last_generated_size;
if (status->last_unsafe) {
return 0;
Expand Down
6 changes: 4 additions & 2 deletions ext/random/tests/03_randomizer/get_int_user.phpt
Expand Up @@ -6,9 +6,11 @@ Random: Randomizer: User Engine results are correctly expanded for getInt()
$randomizer = new \Random\Randomizer (
new class () implements \Random\Engine
{
public $count = 0;

public function generate(): string
{
return "\x01";
return "\x01\x02\x03\x04\x05\x06\x07\x08"[$this->count++];
}
}
);
Expand All @@ -17,4 +19,4 @@ var_dump(bin2hex(pack('V', $randomizer->getInt(0, 0xFFFFFF))));

?>
--EXPECT--
string(8) "01010100"
string(8) "01020300"

0 comments on commit ab5491f

Please sign in to comment.