Skip to content

[Random] fix undefined behaviour part2 #9085

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions ext/random/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,18 @@ static zend_object_handlers random_randomizer_object_handlers;
static inline uint32_t rand_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax)
{
uint32_t result, limit, r;
size_t total_size = 0;
size_t total_size = 0, shift_size = 0;
uint32_t count = 0;

result = 0;
total_size = 0;
do {
r = algo->generate(status);
result = (result << (8 * status->last_generated_size)) | r;
shift_size = (8 * status->last_generated_size);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that my previous comment was wrong, it looks like gcc and clang both treat x << y as x << (y % 32), so this is a behavior change. See:

https://godbolt.org/z/W1xn847Eh

This would be the equivalent here:

shift_size = (8 * status->last_generated_size) % (8 * sizeof(result));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was about to say the same 😄 https://www.ideone.com/ijVb65
So I think you should revert the changes, then just change status->last_generated_size to (status->last_generated_size % sizeof(result)) in the shift?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! It's so complicated 😅
fix again: 2ebbefc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I hadn't seen Ilija's main comment:

The change in behavior might or might not matter, I don't know much about the logic of this function.

Indeed, maybe here you would prefer to handle
result << (8 * status->last_generated_size) not as
result << (8 * (status->last_generated_size % sizeof(result))) but rather as
status->last_generated_size >= sizeof(result) ? 0 : result << (8 * status->last_generated_size)? https://www.ideone.com/Oy1PqS

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional thought: whatever the solution, it should be applied to both rand_range32 and rand_range64.
I see that #9088 does, and also tackles an endianness issue 🙂

if ((8 * sizeof(uint32_t)) < shift_size) {
shift_size = 0;
}
result = (result << shift_size) | r;
total_size += status->last_generated_size;
if (status->last_unsafe) {
return 0;
Expand Down Expand Up @@ -127,7 +131,11 @@ 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;
shift_size = (8 * status->last_generated_size);
if ((8 * sizeof(uint32_t)) < shift_size) {
shift_size = 0;
}
result = (result << shift_size) | r;
total_size += status->last_generated_size;
if (status->last_unsafe) {
return 0;
Expand Down