Harden Proxmox integer parsing bounds

This commit is contained in:
rcourtman 2026-04-01 14:51:00 +01:00
parent cabd224987
commit 030fc20cd6
2 changed files with 65 additions and 9 deletions

View file

@ -88,9 +88,23 @@ func parseFlexibleIntString(raw string) (int, error) {
}
func int64ToInt(v int64) (int, error) {
maxInt := int64(^uint(0) >> 1)
minInt := -maxInt - 1
if v > maxInt || v < minInt {
if strconv.IntSize == 32 {
if v > math.MaxInt32 || v < math.MinInt32 {
return 0, fmt.Errorf("integer %d exceeds int range", v)
}
return int(int32(v)), nil
}
return int(v), nil
}
func uint64ToInt(v uint64) (int, error) {
if strconv.IntSize == 32 {
if v > math.MaxInt32 {
return 0, fmt.Errorf("integer %d exceeds int range", v)
}
return int(int32(v)), nil
}
if v > math.MaxInt64 {
return 0, fmt.Errorf("integer %d exceeds int range", v)
}
return int(v), nil
@ -100,8 +114,13 @@ func floatToIntTrunc(v float64) (int, error) {
if math.IsNaN(v) || math.IsInf(v, 0) {
return 0, fmt.Errorf("non-finite float %v cannot be converted to int", v)
}
limit := math.Exp2(float64(strconv.IntSize - 1))
if v >= limit || v < -limit {
var maxAbs float64
if strconv.IntSize == 32 {
maxAbs = float64(math.MaxInt32) + 1
} else {
maxAbs = float64(math.MaxInt64) + 1
}
if v >= maxAbs || v < -maxAbs {
return 0, fmt.Errorf("float %v exceeds int range", v)
}
return int(v), nil
@ -1681,15 +1700,16 @@ func (a *VMIPAddress) UnmarshalJSON(data []byte) error {
a.Address = coerceString(raw["ip-address"])
maxInt := uint64(^uint(0) >> 1)
prefix, err := coerceUint64("prefix", raw["prefix"])
if err != nil {
prefix = 0
}
if prefix > maxInt {
prefix = maxInt
if prefix > 128 {
prefix = 128
}
if prefixInt, err := uint64ToInt(prefix); err == nil {
a.Prefix = prefixInt
}
a.Prefix = int(prefix)
return nil
}

View file

@ -587,6 +587,42 @@ func TestFlexIntUnmarshalJSONRejectsNonFiniteAndOverflow(t *testing.T) {
}
}
func TestVMIPAddressUnmarshalJSONCapsPrefixToCIDRRange(t *testing.T) {
tests := []struct {
name string
payload string
want int
}{
{
name: "keeps valid prefix",
payload: `{"ip-address":"10.0.0.10","prefix":24}`,
want: 24,
},
{
name: "caps overlong ipv6 prefix",
payload: `{"ip-address":"2001:db8::10","prefix":129}`,
want: 128,
},
{
name: "caps huge prefix before int conversion",
payload: `{"ip-address":"2001:db8::10","prefix":"18446744073709551615"}`,
want: 128,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var got VMIPAddress
if err := json.Unmarshal([]byte(tc.payload), &got); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got.Prefix != tc.want {
t.Fatalf("prefix = %d, want %d", got.Prefix, tc.want)
}
})
}
}
func TestParseUint64Flexible(t *testing.T) {
tests := []struct {
name string