Commit 3cca0692 authored by Rob Pike's avatar Rob Pike

time: allow long fractions in ParseDuration

The code scanned for an integer after a decimal point, which
meant things could overflow if the number was very precise
(0.1234123412341234123412342134s). This fix changes the
parser to stop adding precision once we run out of bits, rather
than trigger an erroneous overflow.

We could parse durations using floating-point arithmetic,
but since the type is int64 and float64 has only has 53 bits
of precision, that would be imprecise.

Fixes #15011.

Change-Id: If85e22b8f6cef12475e221169bb8f493bb9eb590
Reviewed-on: https://go-review.googlesource.com/29338Reviewed-by: 's avatarCostin Chirvasuta <costinc@gmail.com>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent e94c5293
......@@ -1174,6 +1174,37 @@ func leadingInt(s string) (x int64, rem string, err error) {
return x, s[i:], nil
}
// leadingFraction consumes the leading [0-9]* from s.
// It is used only for fractions, so does not return an error on overflow,
// it just stops accumulating precision.
func leadingFraction(s string) (x int64, scale float64, rem string) {
i := 0
scale = 1
overflow := false
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if overflow {
continue
}
if x > (1<<63-1)/10 {
// It's possible for overflow to give a positive number, so take care.
overflow = true
continue
}
y := x*10 + int64(c) - '0'
if y < 0 {
overflow = true
continue
}
x = y
scale *= 10
}
return x, scale, s[i:]
}
var unitMap = map[string]int64{
"ns": int64(Nanosecond),
"us": int64(Microsecond),
......@@ -1236,13 +1267,7 @@ func ParseDuration(s string) (Duration, error) {
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
f, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
for n := pl - len(s); n > 0; n-- {
scale *= 10
}
f, scale, s = leadingFraction(s)
post = pl != len(s)
}
if !pre && !post {
......
......@@ -840,6 +840,10 @@ var parseDurationTests = []struct {
{"9223372036s854ms775us807ns", true, (1<<63 - 1) * Nanosecond},
// large negative value
{"-9223372036854775807ns", true, -1<<63 + 1*Nanosecond},
// huge string; issue 15011.
{"0.100000000000000000000h", true, 6 * Minute},
// This value tests the first overflow check in leadingFraction.
{"0.830103483285477580700h", true, 49*Minute + 48*Second + 372539827*Nanosecond},
// errors
{"", false, 0},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment