internal/export/idna: fix int32 overflows
Prefer multiplication (int64(b)*int64(c) > MaxInt32) over division (b >
MaxInt32/c) for overflow checking as it is a little faster on 386, and a
LOT faster on amd64.
For golang/go#28233.
Change-Id: Ibf42529b93b699417781adc7eca6e66474f00bbf
Reviewed-on: https://go-review.googlesource.com/c/text/+/317731
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Damien Neil <dneil@google.com>
diff --git a/internal/export/idna/punycode.go b/internal/export/idna/punycode.go
index f0cbd48..7e96feb 100644
--- a/internal/export/idna/punycode.go
+++ b/internal/export/idna/punycode.go
@@ -47,6 +47,7 @@
}
}
i, n, bias := int32(0), initialN, initialBias
+ overflow := false
for pos < len(encoded) {
oldI, w := i, int32(1)
for k := base; ; k += base {
@@ -58,29 +59,32 @@
return "", punyError(encoded)
}
pos++
- i += digit * w
- if i < 0 {
+ i, overflow = madd(i, digit, w)
+ if overflow {
return "", punyError(encoded)
}
t := k - bias
- if t < tmin {
+ if k <= bias {
t = tmin
- } else if t > tmax {
+ } else if k >= bias+tmax {
t = tmax
}
if digit < t {
break
}
- w *= base - t
- if w >= math.MaxInt32/base {
+ w, overflow = madd(0, w, base-t)
+ if overflow {
return "", punyError(encoded)
}
}
+ if len(output) >= 1024 {
+ return "", punyError(encoded)
+ }
x := int32(len(output) + 1)
bias = adapt(i-oldI, x, oldI == 0)
n += i / x
i %= x
- if n > utf8.MaxRune || len(output) >= 1024 {
+ if n < 0 || n > utf8.MaxRune {
return "", punyError(encoded)
}
output = append(output, 0)
@@ -113,6 +117,7 @@
if b > 0 {
output = append(output, '-')
}
+ overflow := false
for remaining != 0 {
m := int32(0x7fffffff)
for _, r := range s {
@@ -120,8 +125,8 @@
m = r
}
}
- delta += (m - n) * (h + 1)
- if delta < 0 {
+ delta, overflow = madd(delta, m-n, h+1)
+ if overflow {
return "", punyError(s)
}
n = m
@@ -139,9 +144,9 @@
q := delta
for k := base; ; k += base {
t := k - bias
- if t < tmin {
+ if k <= bias {
t = tmin
- } else if t > tmax {
+ } else if k >= bias+tmax {
t = tmax
}
if q < t {
@@ -162,6 +167,15 @@
return string(output), nil
}
+// madd computes a + (b * c), detecting overflow.
+func madd(a, b, c int32) (next int32, overflow bool) {
+ p := int64(b) * int64(c)
+ if p > math.MaxInt32-int64(a) {
+ return 0, true
+ }
+ return a + int32(p), false
+}
+
func decodeDigit(x byte) (digit int32, ok bool) {
switch {
case '0' <= x && x <= '9':
diff --git a/internal/export/idna/punycode_test.go b/internal/export/idna/punycode_test.go
index 2d99239..5cf0c96 100644
--- a/internal/export/idna/punycode_test.go
+++ b/internal/export/idna/punycode_test.go
@@ -177,6 +177,7 @@
"decode 9999999999a", // "9999999999a" overflows the int32 calculation.
"encode " + strings.Repeat("x", 65536) + "\uff00", // int32 overflow.
+ "encode " + strings.Repeat("x", 65666) + "\uffff", // int32 overflow. issue #28233
}
func TestPunycodeErrors(t *testing.T) {