From 8550765f3889c2afdf2bce0de8f907529328d39f Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Thu, 19 Mar 2026 14:31:08 +0100 Subject: [PATCH] Validate prefix length bounds in DecodePrefix --- shared/netiputil/compact.go | 14 +++++++++++--- shared/netiputil/compact_test.go | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/shared/netiputil/compact.go b/shared/netiputil/compact.go index a40bbb361..a88f6eee2 100644 --- a/shared/netiputil/compact.go +++ b/shared/netiputil/compact.go @@ -26,14 +26,22 @@ func DecodePrefix(b []byte) (netip.Prefix, error) { case 5: var ip4 [4]byte copy(ip4[:], b) - return netip.PrefixFrom(netip.AddrFrom4(ip4), int(b[len(b)-1])), nil + bits := int(b[len(b)-1]) + if bits > 32 { + return netip.Prefix{}, fmt.Errorf("invalid IPv4 prefix length %d (max 32)", bits) + } + return netip.PrefixFrom(netip.AddrFrom4(ip4), bits), nil case 17: var ip6 [16]byte copy(ip6[:], b) addr := netip.AddrFrom16(ip6).Unmap() bits := int(b[len(b)-1]) - if addr.Is4() && bits > 32 { - bits = 32 + if addr.Is4() { + if bits > 32 { + bits = 32 + } + } else if bits > 128 { + return netip.Prefix{}, fmt.Errorf("invalid IPv6 prefix length %d (max 128)", bits) } return netip.PrefixFrom(addr, bits), nil default: diff --git a/shared/netiputil/compact_test.go b/shared/netiputil/compact_test.go index 8e88d4d7d..ddfedfd32 100644 --- a/shared/netiputil/compact_test.go +++ b/shared/netiputil/compact_test.go @@ -110,6 +110,22 @@ func TestDecodePrefixInvalidLength(t *testing.T) { assert.Error(t, err) } +func TestDecodePrefixInvalidBits(t *testing.T) { + // v4 with bits > 32 + b := []byte{10, 0, 0, 1, 33} + _, err := DecodePrefix(b) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid IPv4 prefix length 33") + + // v6 with bits > 128 + b = make([]byte, 17) + b[0] = 0xfd + b[16] = 129 + _, err = DecodePrefix(b) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid IPv6 prefix length 129") +} + func TestDecodePrefixUnmapsV6Input(t *testing.T) { // If someone encodes a v4-mapped v6 as 17 bytes, decode should unmap it // and clamp the prefix length to 32 for v4