Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pkg/sbpf/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,15 @@ func (ip *Interpreter) disassemble(slot Slot, slot2 Slot) string {
case OpLe, OpBe:
return fmt.Sprintf("%s%d r%d", mnemonic, slot.Uimm(), slot.Dst())
case OpJa:
return fmt.Sprintf("ja")
return "ja"
case OpJeqImm, OpJgtImm, OpJgeImm, OpJltImm, OpJleImm, OpJsetImm, OpJneImm, OpJsgtImm, OpJsgeImm, OpJsltImm, OpJsleImm:
return fmt.Sprintf("%s r%d, %d", mnemonic, slot.Dst(), int64(slot.Imm()))
case OpJeqReg, OpJgtReg, OpJgeReg, OpJltReg, OpJleReg, OpJsetReg, OpJneReg, OpJsgtReg, OpJsgeReg, OpJsltReg, OpJsleReg:
return fmt.Sprintf("%s r%d, r%d", mnemonic, slot.Dst(), slot.Src())
case OpCall:
return fmt.Sprintf("call")
return "call"
case OpCallx:
return fmt.Sprintf("callx")
return "callx"
case OpExit:
return "exit"
default:
Expand Down
27 changes: 27 additions & 0 deletions pkg/sbpf/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package sbpf

import (
"errors"
"fmt"
)

// Exception codes.
var (
ExcDivideByZero = errors.New("divide by zero at BPF instruction")
ExcDivideOverflow = errors.New("divide overflow")
ExcOutOfCU = errors.New("compute unit overrun")
ExcCallDepth = errors.New("call depth exceeded")
ExcInvalidInstr = errors.New("invalid instruction - feature not enabled")
ErrOutOfBounds = errors.New("value out of bounds")
ErrInvalidSectionHeader = errors.New("invalid section header")
ExcUnsupportedInstruction = errors.New("unsupported BPF instruction")
)

type ErrStringTooLong struct {
Name string
Len uint64
}

func (e *ErrStringTooLong) Error() string {
return fmt.Sprintf("Section or symbol name `%s` is longer than `%d` bytes", e.Name, e.Len)
}
3 changes: 0 additions & 3 deletions pkg/sbpf/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"bytes"
"debug/elf"
"encoding/binary"
"errors"
"fmt"
"io"

Expand All @@ -16,8 +15,6 @@ import (
"github.com/Overclock-Validator/mithril/pkg/sbpf/sbpfver"
)

var ErrOutOfBounds = errors.New("value out of bounds")

// TODO Fuzz
// TODO Differential fuzz against rbpf

Expand Down
55 changes: 47 additions & 8 deletions pkg/sbpf/loader/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import (
"bufio"
"debug/elf"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"math/bits"
"strings"

"github.com/Overclock-Validator/mithril/pkg/sbpf"
"github.com/Overclock-Validator/mithril/pkg/sbpf/sbpfver"
)

Expand Down Expand Up @@ -90,7 +92,7 @@ func (l *Loader) newSymTableIter(sh *elf.Section64) (*symTableIter, error) {

func (l *Loader) readHeader() error {
if l.fileSize < ehLen {
return ErrOutOfBounds
return sbpf.ErrOutOfBounds
}

var hdrBuf [ehLen]byte
Expand Down Expand Up @@ -291,26 +293,63 @@ func (l *Loader) readSectionHeaderTable() error {
return iter.Err()
}

// Query a single string from a section which is marked as SHT_STRTAB
//
// See https://github.com/anza-xyz/sbpf/blob/main/src/elf_parser/mod.rs#L468
func (l *Loader) getString(strtab *elf.Section64, stroff uint32, maxLen uint16) (string, error) {
if elf.SectionType(strtab.Type) != elf.SHT_STRTAB {
return "", fmt.Errorf("invalid strtab")
return "", sbpf.ErrInvalidSectionHeader
}

offset, carry := bits.Add64(strtab.Off, uint64(stroff), 0)
if carry != 0 {
return "", sbpf.ErrOutOfBounds
}

sectionEnd, carry := bits.Add64(strtab.Off, strtab.Size, 0)
if carry != 0 {
return "", sbpf.ErrOutOfBounds
}

maxEnd, carry := bits.Add64(offset, uint64(maxLen), 0)
if carry != 0 {
return "", sbpf.ErrOutOfBounds
}

if sectionEnd < maxEnd {
maxEnd = sectionEnd
}

if offset > l.fileSize {
return "", sbpf.ErrOutOfBounds
}

readLen := maxEnd - offset
if maxEnd > l.fileSize {
readLen = l.fileSize - offset
}
offset := strtab.Off + uint64(stroff)
if offset > l.fileSize || offset+uint64(maxLen) > l.fileSize {
return "", io.ErrUnexpectedEOF
if readLen == 0 {
return "", sbpf.ErrOutOfBounds
}
rd := bufio.NewReader(io.NewSectionReader(l.rd, int64(offset), int64(maxLen)))

rd := bufio.NewReader(io.NewSectionReader(l.rd, int64(offset), int64(readLen)))
var builder strings.Builder
for {
b, err := rd.ReadByte()
if err != nil {
return "", err
switch {
case errors.Is(err, io.EOF):
return "", &sbpf.ErrStringTooLong{Name: builder.String(), Len: readLen}
default:
return "", err
}
}
if b == 0 {
if b == 0x00 {
break
}
builder.WriteByte(b)
}

return builder.String(), nil
}

Expand Down
70 changes: 70 additions & 0 deletions pkg/sbpf/loader/parse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package loader

import (
"debug/elf"
"testing"

"github.com/Overclock-Validator/mithril/pkg/sbpf"
)

func TestLoader_getString(t *testing.T) {
tests := map[string]struct {
buf []byte
strtab *elf.Section64
stroff uint32
maxLen uint16
want string
wantErr error
}{
"valid string in section": {
buf: []byte(".text\x00"),
strtab: &elf.Section64{Off: 0, Size: 6, Type: uint32(elf.SHT_STRTAB)},
stroff: 0,
maxLen: 16,
want: ".text",
wantErr: nil,
},
"invalid section header": {
buf: []byte(".text\x00"),
strtab: &elf.Section64{Off: 0, Size: 6},
stroff: 0,
maxLen: 16,
want: "",
wantErr: sbpf.ErrInvalidSectionHeader,
},
"out of bounds": {
buf: []byte(".text\x00"),
strtab: &elf.Section64{Off: 6, Size: 6, Type: uint32(elf.SHT_STRTAB)},
stroff: 0,
maxLen: 16,
want: "",
wantErr: sbpf.ErrOutOfBounds,
},
"section header too long": {
buf: []byte(".data.rel.ro\x00"),
strtab: &elf.Section64{Off: 0, Size: 6, Type: uint32(elf.SHT_STRTAB)},
stroff: 0,
maxLen: 16,
want: "",
wantErr: &sbpf.ErrStringTooLong{Name: ".data.", Len: 6},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
l, err := NewLoaderFromBytes(tt.buf)
if err != nil {
t.Fatalf("could not construct receiver type: %v", err)
}
got, gotErr := l.getString(tt.strtab, tt.stroff, tt.maxLen)

if gotErr != nil && gotErr.Error() != tt.wantErr.Error() {
t.Errorf("getString() failed: %+v", gotErr)
return
}

if got != tt.want {
t.Errorf("getString() = %v, want %v", got, tt.want)
}
})
}
}
12 changes: 0 additions & 12 deletions pkg/sbpf/vm.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package sbpf

import (
"errors"
"fmt"

"github.com/Overclock-Validator/mithril/pkg/cu"
Expand Down Expand Up @@ -68,17 +67,6 @@ func (e *Exception) Unwrap() error {
return e.Detail
}

// Exception codes.
var (
ExcDivideByZero = errors.New("divide by zero at BPF instruction")
ExcDivideOverflow = errors.New("divide overflow")
ExcOutOfCU = errors.New("compute unit overrun")
ExcCallDepth = errors.New("call depth exceeded")
ExcInvalidInstr = errors.New("invalid instruction - feature not enabled")

ExcUnsupportedInstruction = errors.New("unsupported BPF instruction")
)

type ExcBadAccess struct {
Addr uint64
Size uint64
Expand Down