diff --git a/ntp.go b/ntp.go index 2582be8..2eab07d 100644 --- a/ntp.go +++ b/ntp.go @@ -919,12 +919,21 @@ func toTimestamp(t time.Time) timestamp { } // toInterval converts an NTP poll interval exponent into a time.Duration. +// The exponent is an int8 from the packet. Negating -128 overflows, and the +// following shift panics. A shift of 34 or more does not fit in a duration +// and wraps negative. Those exponents stay at the nearest duration that fits. func toInterval(t int8) time.Duration { + const maxShift = 33 // time.Second<<34 overflows int64 + shift := int(t) switch { - case t > 0: - return time.Duration(int64(time.Second) << int(t)) - case t < 0: - return time.Duration(int64(time.Second) >> int(-t)) + case shift > maxShift: + return time.Second << maxShift + case shift > 0: + return time.Second << shift + case shift < -29: + return 0 + case shift < 0: + return time.Second >> -shift default: return time.Second } diff --git a/ntp_test.go b/ntp_test.go index 118b5b4..fe5db95 100644 --- a/ntp_test.go +++ b/ntp_test.go @@ -297,3 +297,14 @@ func TestOfflineKissCode(t *testing.T) { assert.Equal(t, kissCode(c.id), c.str) } } + +func TestToIntervalBounds(t *testing.T) { + assert.Equal(t, time.Second, toInterval(0)) + assert.Equal(t, 16*time.Second, toInterval(4)) + assert.Equal(t, 500*time.Millisecond, toInterval(-1)) + assert.Equal(t, time.Nanosecond, toInterval(-29)) + assert.Equal(t, time.Duration(0), toInterval(-30)) + assert.Equal(t, time.Duration(0), toInterval(-128)) + assert.Equal(t, time.Second<<33, toInterval(34)) + assert.Equal(t, time.Second<<33, toInterval(127)) +}