Skip to content
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

More ext-random integer overflow #9190

Closed
iluuu1994 opened this issue Jul 29, 2022 · 6 comments · Fixed by #9197
Closed

More ext-random integer overflow #9190

iluuu1994 opened this issue Jul 29, 2022 · 6 comments · Fixed by #9197

Comments

@iluuu1994
Copy link
Member

Description

https://github.com/php/php-src/runs/7571517985?check_suite_focus=true

========DIFF========
002+ /home/runner/work/php-src/php-src/ext/random/engine_mt19937.c:175:2: runtime error: signed integer overflow: -9223372036854775808 + -9223372036854775808 cannot be represented in type 'long int'
     Random\Engine\Mt19937: success
     Random\Engine\PcgOneseq128XslRr64: success
     Random\Engine\Xoshiro256StarStar: success
--
========DONE========
FAIL Random: Randomizer: serialize [ext/random/tests/03_randomizer/serialize.phpt] 

Note: This error occurred only in the tests with tracing JIT.

/cc @zeriyoshi

Thanks for taking a look!

PHP Version

master

Operating System

No response

@guilliamxavier
Copy link
Contributor

Curious why it didn't pop up earlier, the "offending" RAND_RANGE_BADSCALING (for legacy mode MT_RAND_PHP) hasn't been changed (only moved) since ~ 5 years old ab834f4, which itself only renamed it from RAND_RANGE, which already was ~ 15 years old (at least) and had had some "fixes" like 8a156d9 or cc28589...

@zeriyoshi
Copy link
Contributor

Probably the same problem: #9191

@TimWolla
Copy link
Member

Curious why it didn't pop up earlier, the "offending" RAND_RANGE_BADSCALING (for legacy mode MT_RAND_PHP) hasn't been changed (only moved) since ~ 5 years old

Because mt_rand() was not tested with the full ranges before. Only since the refactoring, were the RNGs methodically exposed with the integer boundaries as inputs. And this issue only really is visible with UBSAN.

@TimWolla
Copy link
Member

TimWolla commented Jul 29, 2022

Standalone reproducer script:

#include<stdint.h>
#include<stdio.h>

typedef int64_t zend_long;
typedef uint64_t zend_ulong;

# define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF))
# define RAND_RANGE_BADSCALING(__n, __min, __max, __tmax) \
	(__n) = (__min) + (zend_long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))

zend_ulong bad_scaling(zend_ulong __n, zend_long __min, zend_long __max, zend_long __tmax)
{
	return (__min)
	+
	(zend_long) (
		(double) ( 
			(double) (__max)
			-
			(__min)
			+
			1.0
		)
		*
		(
			(__n)
			/
			(
				(__tmax)
				+
				1.0
			)
		)
	);
}

int
main(void)
{
	zend_long min, max;
	zend_ulong r;

	min = INT64_MIN;
	max = INT64_MAX;
	r = PHP_MT_RAND_MAX - 1;

	// RAND_RANGE_BADSCALING(r, min, max, PHP_MT_RAND_MAX);
	r = bad_scaling(r, min, max, PHP_MT_RAND_MAX);

	printf("%ld\n", (zend_long) r);
}

results in:

$ clang -fsanitize=undefined test.c; ./a.out
test.c:15:2: runtime error: 1.84467e+19 is outside the range of representable values of type 'long'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test.c:15:2 in 
test.c:14:2: runtime error: signed integer overflow: -9223372036854775808 + -9223372036854775808 cannot be represented in type 'long'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test.c:14:2 in 
0

TimWolla added a commit to TimWolla/php-src that referenced this issue Jul 29, 2022
RAND_RANGE_BADSCALING() invokes undefined behavior when (max - min) >
ZEND_LONG_MAX, because the intermediate `double` might not fit into
`zend_long`.

Fix this by inlining a fixed version of the macro into Mt19937's range()
function. Fixing the macro itself cannot be done in the general case, because
the types of the inputs are not known. Instead of replacing one possibly broken
version with another possibly broken version, the macro is simply left as is
and should be removed in a future version.

The fix itself is simple: Instead of storing the "offset" in a `zend_long`, we
use a `zend_ulong` which is capable of storing the resulting double by
construction. With this fix the implementation of this broken scaling is
effectively identical to the implementation of php_random_range from a data
type perspective, making it easy to verify the correctness.

It was further empirically verified that the broken macro and the fix return
the same results for all possible values of `r` for several distinct pairs of
(min, max).

Fixes phpGH-9190
Fixes phpGH-9191
@TimWolla
Copy link
Member

Okay, this scaling function is absolutely horrible and we really shouldn't have added the support for MT_RAND_PHP to the Mt19937 engine class. Deprecation that is the first thing I'm going to put up for vote in PHP (8.2)+1.

That said: There's a fix in #9197.

TimWolla added a commit to TimWolla/php-src that referenced this issue Aug 3, 2022
RAND_RANGE_BADSCALING() invokes undefined behavior when (max - min) >
ZEND_LONG_MAX, because the intermediate `double` might not fit into
`zend_long`.

Fix this by inlining a fixed version of the macro into Mt19937's range()
function. Fixing the macro itself cannot be done in the general case, because
the types of the inputs are not known. Instead of replacing one possibly broken
version with another possibly broken version, the macro is simply left as is
and should be removed in a future version.

The fix itself is simple: Instead of storing the "offset" in a `zend_long`, we
use a `zend_ulong` which is capable of storing the resulting double by
construction. With this fix the implementation of this broken scaling is
effectively identical to the implementation of php_random_range from a data
type perspective, making it easy to verify the correctness.

It was further empirically verified that the broken macro and the fix return
the same results for all possible values of `r` for several distinct pairs of
(min, max).

Fixes phpGH-9190
Fixes phpGH-9191
TimWolla added a commit that referenced this issue Aug 3, 2022
…9197)

RAND_RANGE_BADSCALING() invokes undefined behavior when (max - min) >
ZEND_LONG_MAX, because the intermediate `double` might not fit into
`zend_long`.

Fix this by inlining a fixed version of the macro into Mt19937's range()
function. Fixing the macro itself cannot be done in the general case, because
the types of the inputs are not known. Instead of replacing one possibly broken
version with another possibly broken version, the macro is simply left as is
and should be removed in a future version.

The fix itself is simple: Instead of storing the "offset" in a `zend_long`, we
use a `zend_ulong` which is capable of storing the resulting double by
construction. With this fix the implementation of this broken scaling is
effectively identical to the implementation of php_random_range from a data
type perspective, making it easy to verify the correctness.

It was further empirically verified that the broken macro and the fix return
the same results for all possible values of `r` for several distinct pairs of
(min, max).

Fixes GH-9190
Fixes GH-9191
@iluuu1994
Copy link
Member Author

Thanks @TimWolla!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants