Fix EOF Validation issues

Fixes a few EOF validation issues in EEST and found by fuzzing.
* subcontainers header with zero subcontainers is invalid
* returning code sections require at least one RETF or qualified JUMPF
* RETF stack must have stack enough for outputs
* JUMPF must not jump into a section with more outputs
* Check stack minimums on RJIMPI and RJUMPV
This commit is contained in:
Danno Ferrin 2024-09-05 15:52:50 -06:00 committed by Marius van der Wijden
parent 71ffe6e6dd
commit 658e4e5559
4 changed files with 24 additions and 5 deletions

View file

@ -170,7 +170,7 @@ func ExecuteTest(src []byte) (int, int, error) {
return 0, 0, err return 0, 0, err
} }
passed, total := 0, 0 passed, total := 0, 0
for _, tests := range testsByName { for testsName, tests := range testsByName {
for name, tt := range tests.Vectors { for name, tt := range tests.Vectors {
for fork, r := range tt.Results { for fork, r := range tt.Results {
total++ total++
@ -181,11 +181,11 @@ func ExecuteTest(src []byte) (int, int, error) {
err = err2 err = err2
} }
if r.Result && err != nil { if r.Result && err != nil {
fmt.Fprintf(os.Stderr, "%s, %s: expected success, got %v\n", name, fork, err) fmt.Fprintf(os.Stderr, "%s %s, %s: expected success, got %v\n", testsName, name, fork, err)
continue continue
} }
if !r.Result && err == nil { if !r.Result && err == nil {
fmt.Fprintf(os.Stderr, "%s, %s: expected error %s, got %v\n", name, fork, r.Exception, err) fmt.Fprintf(os.Stderr, "%s %s, %s: expected error %s, got %v\n", testsName, name, fork, r.Exception, err)
continue continue
} }
/* /*

View file

@ -206,6 +206,9 @@ func (c *Container) unmarshalSubContainer(b []byte, isInitcode bool, topLevel bo
if kind != kindContainer { if kind != kindContainer {
panic("somethings wrong") panic("somethings wrong")
} }
if len(containerSizes) == 0 {
return fmt.Errorf("%w: total container count must not be zero", ErrInvalidContainerSectionSize)
}
offset = offset + 2 + 2*len(containerSizes) + 1 offset = offset + 2 + 2*len(containerSizes) + 1
} }

View file

@ -42,6 +42,7 @@ var (
ErrInvalidEOFInitcode = errors.New("invalid eof initcode") ErrInvalidEOFInitcode = errors.New("invalid eof initcode")
ErrNonceUintOverflow = errors.New("nonce uint64 overflow") ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
ErrInvalidNumberOfOutputs = errors.New("invalid number of outputs") ErrInvalidNumberOfOutputs = errors.New("invalid number of outputs")
ErrInvalidNonReturningFlag = errors.New("Invalid non-returning flag, bad RETF")
// errStopToken is an internal token indicating interpreter loop termination, // errStopToken is an internal token indicating interpreter loop termination,
// never returned to outside callers. // never returned to outside callers.

View file

@ -26,6 +26,7 @@ func validateControlFlow2(code []byte, section int, metadata []*FunctionMetadata
// set the initial stack bounds // set the initial stack bounds
setBounds(0, int(metadata[section].Input), int(metadata[section].Input)) setBounds(0, int(metadata[section].Input), int(metadata[section].Input))
qualifiedExit := false
for pos := 0; pos < len(code); pos++ { for pos := 0; pos < len(code); pos++ {
op := OpCode(code[pos]) op := OpCode(code[pos])
currentBounds := stackBounds[pos] currentBounds := stackBounds[pos]
@ -62,9 +63,14 @@ func validateControlFlow2(code []byte, section int, metadata []*FunctionMetadata
if currentBounds.max != currentBounds.min { if currentBounds.max != currentBounds.min {
return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", ErrInvalidNumberOfOutputs, currentBounds.max, currentBounds.min, pos) return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", ErrInvalidNumberOfOutputs, currentBounds.max, currentBounds.min, pos)
} }
if have, want := int(metadata[section].Output), currentBounds.min; have != want { have := int(metadata[section].Output)
if have >= maxOutputItems {
return 0, fmt.Errorf("%w: at pos %d", ErrInvalidNonReturningFlag, pos)
}
if want := currentBounds.min; have != want {
return 0, fmt.Errorf("%w: have %d, want %d, at pos %d", ErrInvalidOutputs, have, want, pos) return 0, fmt.Errorf("%w: have %d, want %d, at pos %d", ErrInvalidOutputs, have, want, pos)
} }
qualifiedExit = true
case JUMPF: case JUMPF:
arg, _ := parseUint16(code[pos+1:]) arg, _ := parseUint16(code[pos+1:])
newSection := metadata[arg] newSection := metadata[arg]
@ -76,6 +82,9 @@ func validateControlFlow2(code []byte, section int, metadata []*FunctionMetadata
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos) return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
} }
} else { } else {
if metadata[section].Output < newSection.Output {
return 0, fmt.Errorf("%w: at pos %d", ErrInvalidNumberOfOutputs, pos)
}
if currentBounds.max != currentBounds.min { if currentBounds.max != currentBounds.min {
return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", ErrInvalidNumberOfOutputs, currentBounds.max, currentBounds.min, pos) return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", ErrInvalidNumberOfOutputs, currentBounds.max, currentBounds.min, pos)
} }
@ -83,6 +92,7 @@ func validateControlFlow2(code []byte, section int, metadata []*FunctionMetadata
return 0, fmt.Errorf("%w: at pos %d", ErrInvalidNumberOfOutputs, pos) return 0, fmt.Errorf("%w: at pos %d", ErrInvalidNumberOfOutputs, pos)
} }
} }
qualifiedExit = qualifiedExit || newSection.Output < maxOutputItems
case DUPN: case DUPN:
arg := int(code[pos+1]) + 1 arg := int(code[pos+1]) + 1
if want, have := arg, currentBounds.min; want > have { if want, have := arg, currentBounds.min; want > have {
@ -175,12 +185,14 @@ func validateControlFlow2(code []byte, section int, metadata []*FunctionMetadata
// target reached via backwards jump // target reached via backwards jump
nextBounds, ok := stackBounds[nextPC] nextBounds, ok := stackBounds[nextPC]
if !ok { if !ok {
fmt.Print("2")
return 0, ErrInvalidBackwardJump return 0, ErrInvalidBackwardJump
} }
if currentStackMax != nextBounds.max { if currentStackMax != nextBounds.max {
return 0, fmt.Errorf("%w want %d as current max got %d at pos %d,", ErrInvalidBackwardJump, currentStackMax, nextBounds.max, pos) return 0, fmt.Errorf("%w want %d as current max got %d at pos %d,", ErrInvalidBackwardJump, currentStackMax, nextBounds.max, pos)
} }
if currentStackMin != nextBounds.min {
return 0, fmt.Errorf("%w want %d as current min got %d at pos %d,", ErrInvalidBackwardJump, currentStackMin, nextBounds.min, pos)
}
} }
} }
} }
@ -192,6 +204,9 @@ func validateControlFlow2(code []byte, section int, metadata []*FunctionMetadata
} }
} }
if qualifiedExit != (metadata[section].Output < maxOutputItems) {
return 0, fmt.Errorf("%w no RETF or qualified JUMPF", ErrInvalidNonReturningFlag)
}
if maxStackHeight >= int(params.StackLimit) { if maxStackHeight >= int(params.StackLimit) {
return 0, ErrStackOverflow{maxStackHeight, int(params.StackLimit)} return 0, ErrStackOverflow{maxStackHeight, int(params.StackLimit)}
} }