Skip to content

Commit e66a4d6

Browse files
committed
fix(enum): bounds-check ValueList index to avoid runtime panic
A ValueList enum decodes its variant index from a single untrusted input byte (0-255). The final branch of Enum.Process did an unguarded slice access e.ValueList[e.Index], so an index beyond the value list (e.g. byte 0x10 into a 3-variant enum) panicked with an opaque runtime 'index out of range' inside the decoder. The TypeMapping.Types path above already guards this; the ValueList path did not. Add the matching bounds check with a descriptive message so a caller recovering decode panics gets the offending type and index. In-range decoding is unchanged. Adds a regression test.
1 parent 5877f7a commit e66a4d6

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

types/Enum.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ func (e *Enum) Process() {
6969
return
7070
}
7171
}
72+
if e.Index >= len(e.ValueList) {
73+
panic(fmt.Errorf("enum %s index out of range [%d] with value list length %d", e.TypeName, e.Index, len(e.ValueList)))
74+
}
7275
if e.ValueList[e.Index] != "" {
7376
e.Value = e.ValueList[e.Index]
7477
}

types/Enum_valuelist_oob_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package types
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"testing"
7+
8+
"github.com/itering/scale.go/source"
9+
scaleBytes "github.com/itering/scale.go/types/scaleBytes"
10+
"github.com/itering/scale.go/utiles"
11+
)
12+
13+
// A ValueList enum decodes its variant index from a single untrusted input byte
14+
// (0-255). If that index is beyond the value list, the decoder previously did an
15+
// unguarded slice access e.ValueList[e.Index] and panicked with an opaque
16+
// "runtime error: index out of range". This mirrors the descriptive bounds check
17+
// already present on the TypeMapping.Types path so the recovering caller gets the
18+
// offending type/index instead of a bare runtime panic.
19+
func TestEnumValueListIndexOutOfRange(t *testing.T) {
20+
RegCustomTypes(map[string]source.TypeStruct{
21+
"OOBValueEnum": {Type: "enum", ValueList: []string{"A", "B", "C"}},
22+
})
23+
24+
// Out-of-range index byte (0x10 = 16, list length 3) -> descriptive panic.
25+
func() {
26+
defer func() {
27+
r := recover()
28+
if r == nil {
29+
t.Fatal("expected a panic for an out-of-range enum index")
30+
}
31+
msg := fmt.Sprint(r)
32+
if !strings.Contains(msg, "index out of range") ||
33+
!strings.Contains(msg, "value list length") {
34+
t.Fatalf("panic message not descriptive: %v", r)
35+
}
36+
}()
37+
m := ScaleDecoder{}
38+
m.Init(scaleBytes.ScaleBytes{Data: utiles.HexToBytes("0x10")}, nil)
39+
m.ProcessAndUpdateData("OOBValueEnum")
40+
}()
41+
42+
// In-range index still decodes correctly.
43+
m := ScaleDecoder{}
44+
m.Init(scaleBytes.ScaleBytes{Data: utiles.HexToBytes("0x01")}, nil)
45+
if got := m.ProcessAndUpdateData("OOBValueEnum"); got != "B" {
46+
t.Fatalf("in-range decode = %v, want B", got)
47+
}
48+
}

0 commit comments

Comments
 (0)