summaryrefslogtreecommitdiff
path: root/debian/patches/zend_int_overflow.patch
blob: 591f2244af35a346f8f5b87139b6fb36d702a8bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
Author: Sean Finney <seanius@debian.org>
Description: Another integer overflow/underflow logic fix.
 Once again, don't rely on undefined behavior and instead detect
 the overflow/underflow conditions intelligently.
Bug: http://bugs.php.net/bug.php?id=51008
Bug-Debian: http://bugs.debian.org/570144
--- php.orig/Zend/zend_hash.h
+++ php/Zend/zend_hash.h
@@ -306,9 +306,11 @@ END_EXTERN_C()
 
 #define ZEND_HANDLE_NUMERIC(key, length, func) do {							\
 	register const char *tmp = key;											\
+	int negative = 0;											\
 																			\
 	if (*tmp == '-') {														\
 		tmp++;																\
+		negative = 1;																\
 	}																		\
 	if (*tmp >= '0' && *tmp <= '9') { /* possibly a numeric index */		\
 		const char *end = key + length - 1;									\
@@ -322,19 +324,19 @@ END_EXTERN_C()
 		     *tmp > '2')) { /* overflow */									\
 			break;															\
 		}																	\
-		idx = (*tmp - '0');													\
+		idx = ((negative)?-1:1) * (*tmp - '0');													\
 		while (++tmp != end && *tmp >= '0' && *tmp <= '9') {				\
-			idx = (idx * 10) + (*tmp - '0');								\
+			int digit = (*tmp - '0');								\
+			if ( (!negative) && idx <= (LONG_MAX-digit)/10 ) {					\
+				idx = (idx * 10) + digit;								\
+			} else if ( (negative) && idx >= (LONG_MIN+digit)/10 ) {				\
+				idx = (idx * 10) - digit;								\
+			} else {																\
+				--tmp; /* overflow or underflow, make sure tmp != end */			\
+				break;																\
+			}																\
 		}																	\
 		if (tmp == end) {													\
-			if (*key == '-') {												\
-				idx = -idx;													\
-				if (idx > 0) { /* overflow */								\
-					break;													\
-				}															\
-			} else if (idx < 0) { /* overflow */							\
-				break;														\
-			}																\
 			return func;													\
 		}																	\
 	}																		\
--- php.orig/Zend/tests/bug45877.phpt
+++ php/Zend/tests/bug45877.phpt
@@ -1,23 +1,40 @@
 --TEST--
 Bug #45877 (Array key '2147483647' left as string)
---INI--
-precision=16
 --FILE--
 <?php
-$keys = array(PHP_INT_MAX,
-	(string) PHP_INT_MAX,
-	(string) (-PHP_INT_MAX - 1),
-	-PHP_INT_MAX - 1,
-	(string) (PHP_INT_MAX + 1));
+$max = sprintf("%d", PHP_INT_MAX);
+switch($max) {
+case "2147483647": /* 32-bit systems */
+	$min = "-2147483648";
+	$overflow = "2147483648";
+	$underflow = "-2147483649";
+	break;
+case "9223372036854775807": /* 64-bit systems */
+	$min = "-9223372036854775808";
+	$overflow = "9223372036854775808";
+	$underflow = "-9223372036854775809";
+	break;
+default:
+	die("failed: unknown value for PHP_MAX_INT");
+	break;
+}
 
-var_dump(array_fill_keys($keys, 1));
-?>
---EXPECTF--
-array(3) {
-  [%d7]=>
-  int(1)
-  [-%d8]=>
-  int(1)
-  ["%s"]=>
-  int(1)
+function test_value($val, $msg) {
+	$a = array($val => 1);
+	$keys = array_keys($a);
+	if ($val == $keys[0]) $result = "ok";
+	else $result = "failed ($val != $keys[0])";
+	echo "$msg: $result\n";
 }
+
+test_value($max, "max");
+test_value($overflow, "overflow");
+test_value($min, "min");
+test_value($underflow, "underflow");
+
+?>
+--EXPECT--
+max: ok
+overflow: ok
+min: ok
+underflow: ok