php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Return to Bug #64450
Patch bug64450.patch revision 2013-03-21 20:00 UTC by ab@php.net
revision 2013-03-21 14:03 UTC by ab@php.net

Patch bug64450.patch for *General Issues Bug #64450

Patch version 2013-03-21 14:03 UTC

Return to Bug #64450 | Download this patch
This patch is obsolete

Obsoleted by patches:

Patch Revisions:

Developer: ab@php.net

diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h
index e831f32..bdeaed1 100644
--- a/ext/standard/php_rand.h
+++ b/ext/standard/php_rand.h
@@ -43,6 +43,10 @@
 #define RAND_RANGE(__n, __min, __max, __tmax) \
     (__n) = (__min) + (long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
 
+#define RAND_RANGE_DOUBLE(__n, __min, __max, __tmax) \
+    (__n) = (__min) + ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0))
+
+
 /* MT Rand */
 #define PHP_MT_RAND_MAX ((long) (0x7FFFFFFF)) /* (1<<31) - 1 */ 
 
diff --git a/ext/standard/rand.c b/ext/standard/rand.c
index 5f55a41..f716bc6 100644
--- a/ext/standard/rand.c
+++ b/ext/standard/rand.c
@@ -310,16 +310,15 @@ PHP_FUNCTION(rand)
    Returns a random number from Mersenne Twister */
 PHP_FUNCTION(mt_rand)
 {
-	long min;
-	long max;
-	long number;
+	double min, max, number_one;
+	long number_two;
 	int  argc = ZEND_NUM_ARGS();
 
 	if (argc != 0) {
-		if (zend_parse_parameters(argc TSRMLS_CC, "ll", &min, &max) == FAILURE) {
+		if (zend_parse_parameters(argc TSRMLS_CC, "dd", &min, &max) == FAILURE) {
 			return;
 		} else if (max < min) {
-			php_error_docref(NULL TSRMLS_CC, E_WARNING, "max(%ld) is smaller than min(%ld)", max, min);
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "max(%.0f) is smaller than min(%.0f)", max, min);
 			RETURN_FALSE;
 		}
 	}
@@ -328,20 +327,28 @@ PHP_FUNCTION(mt_rand)
 		php_mt_srand(GENERATE_SEED() TSRMLS_CC);
 	}
 
-	/*
-	 * Melo: hmms.. randomMT() returns 32 random bits...
-	 * Yet, the previous php_rand only returns 31 at most.
-	 * So I put a right shift to loose the lsb. It *seems*
-	 * better than clearing the msb. 
-	 * Update: 
-	 * I talked with Cokus via email and it won't ruin the algorithm
-	 */
-	number = (long) (php_mt_rand(TSRMLS_C) >> 1);
 	if (argc == 2) {
-		RAND_RANGE(number, min, max, PHP_MT_RAND_MAX);
+		/* process the potentially overflowing variant*/
+		number_one = (double) (php_mt_rand(TSRMLS_C) >> 1);
+		RAND_RANGE_DOUBLE(number_one, min, max, PHP_MT_RAND_MAX);
+		if (number_one <= LONG_MAX) {
+			RETURN_LONG((long)floor(number_one));
+		} else {
+			RETURN_DOUBLE(floor(number_one));
+		}
+	} else {
+		/*
+		* Melo: hmms.. randomMT() returns 32 random bits...
+		* Yet, the previous php_rand only returns 31 at most.
+		* So I put a right shift to loose the lsb. It *seems*
+		* better than clearing the msb.
+		* Update:
+		* I talked with Cokus via email and it won't ruin the algorithm
+		*/
+		number_two = (long) (php_mt_rand(TSRMLS_C) >> 1);
+		RETURN_LONG(number_two);
 	}
 
-	RETURN_LONG(number);
 }
 /* }}} */
 
--- /dev/null	Thu Mar 21 14:20:31 2013
+++ ext/standard/tests/general_functions/bug64450.phpt	Thu Mar 21 14:19:45 2013
@@ -0,0 +1,61 @@
+--TEST--
+Bug #64450 (mt_rand causes overflow within certain max value).
+--FILE--
+<?php
+	echo "var 1\n";
+	$min = 0;
+	$max = pow(10, 12);
+	$rand = mt_rand($min, $max);
+	var_dump($min, $max, $rand, $rand >= $min, $max >= $rand);
+
+	echo "var 2\n";
+	$min = 0;
+	$max = pow(10, 13);
+	$rand = mt_rand($min, $max);
+	var_dump($min, $max, $rand, $rand >= $min, $max >= $rand);
+
+	/* this should be always in the int range */
+	echo "var 3\n";
+	$min = 0;
+	$max = PHP_INT_MAX;
+	$rand = mt_rand($min, $max);
+	var_dump($min, $max, $rand, $rand >= $min, $max >= $rand);
+
+	echo "var 4\n";
+	$min = 0;
+	$max = PHP_INT_MAX+1;
+	$rand = mt_rand($min, $max);
+	var_dump($min, $max, $rand, $rand >= $min, $max >= $rand);
+
+	echo "var 5\n";
+	var_dump(mt_rand());
+?>
+==DONE==
+--EXPECTF--
+var 1
+int(0)
+%s(1000000000000)
+%s(%d)
+bool(true)
+bool(true)
+var 2
+int(0)
+%s(10000000000000)
+%s(%d)
+bool(true)
+bool(true)
+var 3
+int(0)
+int(%d)
+int(%d)
+bool(true)
+bool(true)
+var 4
+int(0)
+%s(%d)
+%s(%d)
+bool(true)
+bool(true)
+var 5
+int(%d)
+==DONE==
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat May 18 06:01:34 2024 UTC