core/vm: clean up code and containerOffset handling

This commit is contained in:
Marius van der Wijden 2025-02-07 14:07:03 +01:00
parent 5bfacf4ba2
commit 644823db48
4 changed files with 61 additions and 74 deletions

View File

@ -67,13 +67,10 @@ func isEOFVersion1(code []byte) bool {
// Container is an EOF container object.
type Container struct {
types []*functionMetadata
codeSectionOffsets []int
codeSectionEnd int
codeSectionOffsets []int // contains all the offsets of the codeSections. The last item marks the end
subContainers []*Container
subContainerOffsets []int
subContainerEnd int
dataOffest int
dataSize int // might be more than len(data)
subContainerOffsets []int // contains all the offsets of the subContainers. The last item marks the end
dataSize int // might be more than len(data)
rawContainer []byte
}
@ -108,48 +105,44 @@ func (meta *functionMetadata) checkStackMax(stackMax int) error {
return nil
}
func (c *Container) codeSectionSize(s int) int {
if s >= len(c.codeSectionOffsets) || s < 0 {
return 0
} else if s == len(c.codeSectionOffsets)-1 {
return c.codeSectionEnd - c.codeSectionOffsets[s]
}
return c.codeSectionOffsets[s+1] - c.codeSectionOffsets[s]
}
func (c *Container) codeSectionBytes(s int) []byte {
if s >= len(c.codeSectionOffsets) || s < 0 {
// codeSectionAt returns the code section at index.
// returns an empty slice if the index is out of bounds.
func (c *Container) codeSectionAt(index int) []byte {
if index >= len(c.codeSectionOffsets)-1 || index < 0 {
return c.rawContainer[0:0]
} else if s == len(c.codeSectionOffsets)-1 {
return c.rawContainer[c.codeSectionOffsets[s]:c.codeSectionEnd]
}
return c.rawContainer[c.codeSectionOffsets[s]:c.codeSectionOffsets[s+1]]
return c.rawContainer[c.codeSectionOffsets[index]:c.codeSectionOffsets[index+1]]
}
func (c *Container) subContainerSize(s int) int {
if s >= len(c.subContainerOffsets) || s < 0 {
return 0
} else if s == len(c.subContainerOffsets)-1 {
return c.subContainerEnd - c.subContainerOffsets[s]
}
return c.subContainerOffsets[s+1] - c.subContainerOffsets[s]
}
func (c *Container) subContainerBytes(s int) []byte {
if s >= len(c.subContainerOffsets) || s < 0 {
// subContainerAt returns the sub container at index.
// returns an empty slice if the index is out of bounds.
func (c *Container) subContainerAt(index int) []byte {
if index >= len(c.subContainerOffsets)-1 || index < 0 {
return c.rawContainer[0:0]
} else if s == len(c.subContainerOffsets)-1 {
return c.rawContainer[c.subContainerOffsets[s]:c.subContainerEnd]
}
return c.rawContainer[c.subContainerOffsets[s]:c.subContainerOffsets[s+1]]
return c.rawContainer[c.subContainerOffsets[index]:c.subContainerOffsets[index+1]]
}
func (c *Container) dataOffset() int {
if len(c.subContainerOffsets) > 0 {
return c.subContainerOffsets[len(c.subContainerOffsets)-1]
}
return c.codeSectionOffsets[len(c.codeSectionOffsets)-1]
}
func (c *Container) dataLen() int {
return len(c.rawContainer) - c.dataOffest
return len(c.rawContainer) - c.dataOffset()
}
func (c *Container) getDataAt(offset, length uint64) []byte {
return getData(c.rawContainer, uint64(c.dataOffset())+offset, length)
}
// MarshalBinary encodes an EOF container into binary format.
func (c *Container) MarshalBinary() []byte {
// Drop the end markers
codeSectionOffsets := c.codeSectionOffsets[:len(c.codeSectionOffsets)-1]
// Build EOF prefix.
b := make([]byte, 2)
copy(b, eofMagic)
@ -159,9 +152,9 @@ func (c *Container) MarshalBinary() []byte {
b = append(b, kindTypes)
b = binary.BigEndian.AppendUint16(b, uint16(len(c.types)*4))
b = append(b, kindCode)
b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSectionOffsets)))
for s := range c.codeSectionOffsets {
b = binary.BigEndian.AppendUint16(b, uint16(c.codeSectionSize(s)))
b = binary.BigEndian.AppendUint16(b, uint16(len(codeSectionOffsets)))
for s := range codeSectionOffsets {
b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSectionAt(s))))
}
var encodedContainer [][]byte
if len(c.subContainers) != 0 {
@ -181,13 +174,13 @@ func (c *Container) MarshalBinary() []byte {
for _, ty := range c.types {
b = append(b, []byte{ty.inputs, ty.outputs, byte(ty.maxStackHeight >> 8), byte(ty.maxStackHeight & 0x00ff)}...)
}
for s := range c.codeSectionOffsets {
b = append(b, c.codeSectionBytes(s)...)
for s := range codeSectionOffsets {
b = append(b, c.codeSectionAt(s)...)
}
for _, section := range encodedContainer {
b = append(b, section...)
}
b = append(b, c.rawContainer[c.dataOffest:]...)
b = append(b, c.rawContainer[c.dataOffset():]...)
return b
}
@ -333,8 +326,8 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool)
codeSectionOffsets[i] = idx
idx += size
}
c.codeSectionOffsets = codeSectionOffsets
c.codeSectionEnd = idx
// add the end marker to the codeSection offsets
c.codeSectionOffsets = append(codeSectionOffsets, idx)
// Parse the optional container sizes.
if len(containerSizes) != 0 {
if len(containerSizes) > maxContainerSections {
@ -360,15 +353,14 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool)
idx += size
}
c.subContainers = subContainers
c.subContainerEnd = idx
c.subContainerOffsets = subContainerOffsets
// add the end marker to the subContainer offsets
c.subContainerOffsets = append(subContainerOffsets, idx)
}
//Parse data section.
if topLevel && len(b) != idx+dataSize {
return errTruncatedTopLevelContainer
}
c.dataOffest = idx
c.rawContainer = b
@ -398,7 +390,7 @@ func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error {
// should not mean 2 and 3 should be visited twice
var (
index = toVisit[0]
code = c.codeSectionBytes(index)
code = c.codeSectionAt(index)
)
if _, ok := visited[index]; !ok {
res, err := validateCode(code, index, c, jt, refBy == refByEOFCreate)
@ -430,7 +422,7 @@ func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error {
toVisit = toVisit[1:]
}
// Make sure every code section is visited at least once.
if len(visited) != len(c.codeSectionOffsets) {
if len(visited) != len(c.codeSectionOffsets)-1 {
return errUnreachableCode
}
for idx, container := range c.subContainers {
@ -507,6 +499,8 @@ func sum(list []int) (s int) {
}
func (c *Container) String() string {
// Drop the end markers
codeSectionOffsets := c.codeSectionOffsets[:len(c.codeSectionOffsets)-1]
var output = []string{
"Header",
fmt.Sprintf(" - EOFMagic: %02x", eofMagic),
@ -514,14 +508,13 @@ func (c *Container) String() string {
fmt.Sprintf(" - KindType: %02x", kindTypes),
fmt.Sprintf(" - TypesSize: %04x", len(c.types)*4),
fmt.Sprintf(" - KindCode: %02x", kindCode),
fmt.Sprintf(" - Number of code sections: %d", len(codeSectionOffsets)),
fmt.Sprintf(" - KindData: %02x", kindData),
fmt.Sprintf(" - DataSize: %04x", c.dataLen()),
fmt.Sprintf(" - Number of code sections: %d", len(c.codeSectionOffsets)),
fmt.Sprintf(" - DataSize: %04x", c.dataSize),
}
for i := range c.codeSectionOffsets {
output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, c.codeSectionSize(i)))
for i := range codeSectionOffsets {
output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, len(c.codeSectionAt(i))))
}
output = append(output, fmt.Sprintf(" - Number of subcontainers: %d", len(c.subContainers)))
if len(c.subContainers) > 0 {
for i, section := range c.subContainers {
@ -533,12 +526,12 @@ func (c *Container) String() string {
output = append(output, fmt.Sprintf(" - Type %v: %x", i,
[]byte{typ.inputs, typ.outputs, byte(typ.maxStackHeight >> 8), byte(typ.maxStackHeight & 0x00ff)}))
}
for i := range c.codeSectionOffsets {
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, c.codeSectionBytes(i)))
for i := range codeSectionOffsets {
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, c.codeSectionAt(i)))
}
for i, section := range c.subContainers {
output = append(output, fmt.Sprintf(" - Subcontainer %d: %x", i, section.MarshalBinary()))
}
output = append(output, fmt.Sprintf(" - Data: %#x", c.rawContainer[c.dataOffest:]))
output = append(output, fmt.Sprintf(" - Data: %#x", c.rawContainer[c.dataOffset():]))
return strings.Join(output, "\n")
}

View File

@ -134,10 +134,11 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
if int(idx) >= len(scope.Contract.Container.subContainerOffsets) {
return nil, fmt.Errorf("invalid subcontainer")
}
subContainer := scope.Contract.Container.subContainerAt(int(idx))
// Deduct hashing charge
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
hashingCharge := (params.Keccak256WordGas) * ((uint64(scope.Contract.Container.subContainerSize(int(idx))) + 31) / 32)
hashingCharge := (params.Keccak256WordGas) * ((uint64(len(subContainer)) + 31) / 32)
if ok := scope.Contract.UseGas(hashingCharge, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified); !ok {
return nil, ErrGasUintOverflow
}
@ -154,7 +155,7 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
// Skip the immediate
*pc += 1
res, addr, returnGas, suberr := interpreter.evm.EOFCreate(scope.Contract, input, scope.Contract.Container.subContainerBytes(int(idx)), gas, &value, &salt)
res, addr, returnGas, suberr := interpreter.evm.EOFCreate(scope.Contract, input, subContainer, gas, &value, &salt)
if suberr != nil {
stackvalue.Clear()
} else {
@ -185,7 +186,7 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
return nil, fmt.Errorf("invalid subcontainer")
}
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
containerCode := scope.Contract.Container.subContainerBytes(int(idx))
containerCode := scope.Contract.Container.subContainerAt(int(idx))
if len(containerCode) == 0 {
return nil, errors.New("nonexistant subcontainer")
}
@ -225,7 +226,7 @@ func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
stackItem.Clear()
scope.Stack.push(&stackItem)
} else {
data := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+offset, 32)
data := scope.Contract.Container.getDataAt(offset, 32)
scope.Stack.push(stackItem.SetBytes(data))
}
return nil, nil
@ -236,7 +237,7 @@ func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
var (
offset = uint64(binary.BigEndian.Uint16(scope.Contract.Code[*pc+1:]))
)
data := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+offset, 32)
data := scope.Contract.Container.getDataAt(offset, 32)
scope.Stack.push(new(uint256.Int).SetBytes(data))
*pc += 2 // move past 2 byte immediate
return nil, nil
@ -258,7 +259,7 @@ func opDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
)
// These values are checked for overflow during memory expansion calculation
// (the memorySize function on the opcode).
data := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+offset.Uint64(), size.Uint64())
data := scope.Contract.Container.getDataAt(offset.Uint64(), size.Uint64())
scope.Memory.Set(memOffset.Uint64(), size.Uint64(), data)
return nil, nil
}

View File

@ -40,10 +40,9 @@ func MakeTestContainer(
idx += len(code)
testBytes = append(testBytes, code...)
}
codeSectionEnd := idx
codeSectionOffsets = append(codeSectionOffsets, idx)
var subContainerOffsets []int
subContainerEnd := 0
if len(subContainers) > 0 {
subContainerOffsets = make([]int, len(subContainers))
for _, subContainer := range subContainers {
@ -52,7 +51,8 @@ func MakeTestContainer(
idx += len(containerBytes)
testBytes = append(testBytes, containerBytes...)
}
subContainerEnd = idx
// set the subContainer end marker
subContainerOffsets = append(subContainerOffsets, idx)
}
testBytes = append(testBytes, data...)
@ -60,11 +60,8 @@ func MakeTestContainer(
return Container{
types: types,
codeSectionOffsets: codeSectionOffsets,
codeSectionEnd: codeSectionEnd,
subContainers: subContainers,
subContainerOffsets: subContainerOffsets,
subContainerEnd: subContainerEnd,
dataOffest: subContainerEnd,
dataSize: dataSize,
rawContainer: testBytes,
}
@ -78,9 +75,7 @@ func TestEOFMarshaling(t *testing.T) {
{
want: Container{
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
codeSectionOffsets: []int{19}, // 604200
codeSectionEnd: 22,
dataOffest: 22,
codeSectionOffsets: []int{19, 22}, // 604200, endMarker
dataSize: 3,
rawContainer: common.Hex2Bytes("ef000101000402000100030400030000800001604200010203"),
},
@ -92,9 +87,7 @@ func TestEOFMarshaling(t *testing.T) {
{inputs: 2, outputs: 3, maxStackHeight: 4},
{inputs: 1, outputs: 1, maxStackHeight: 1},
},
codeSectionOffsets: []int{31, 34, 39}, // 604200, 6042604200, 00
codeSectionEnd: 40,
dataOffest: 40,
codeSectionOffsets: []int{31, 34, 39, 40}, // 604200, 6042604200, 00, endMarker
rawContainer: common.Hex2Bytes("ef000101000c02000300030005000104000000008000010203000401010001604200604260420000"),
},
},

BIN
statetest

Binary file not shown.