Skip to content

Commit

Permalink
add ego_largeheap build tag as workaround for heap sizes larger than …
Browse files Browse the repository at this point in the history
…16GB
  • Loading branch information
thomasten committed Sep 25, 2023
1 parent 411e328 commit d0ec57e
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 9 deletions.
46 changes: 39 additions & 7 deletions ego/cli/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import (
// ErrErrUnsupportedImportEClient is returned when an EGo binary uses the eclient package instead of the enclave package.
var ErrUnsupportedImportEClient = errors.New("unsupported import: github.com/edgelesssys/ego/eclient")

// ErrLargeHeapWithSmallHeapSize is returned when a binary is built with ego_largehap, but the heap size is set to less than 512MB.
var ErrLargeHeapWithSmallHeapSize = errors.New("large heap is enabled, but heapSize is too small")

// ErrNoLargeHeapWithLargeHeapSize is returned when a binary is built without ego_largeheap, but the heap size is set to more than 16GB.
var ErrNoLargeHeapWithLargeHeapSize = errors.New("this heapSize requires large heap mode")

func (c *Cli) embedConfigAsPayload(path string, jsonData []byte) error {
// Load ELF executable
f, err := c.fs.OpenFile(path, os.O_RDWR, 0)
Expand Down Expand Up @@ -106,33 +112,59 @@ func getPayloadInformation(f io.ReaderAt) (uint64, int64, int64, error) {
return payloadSize, int64(payloadOffset), int64(oeInfo.Offset), nil
}

// checkUnsupportedImports checks whether the to-be-signed or to-be-executed binary uses Go imports which are not supported.
func (c *Cli) checkUnsupportedImports(path string) error {
func (c *Cli) getSymbolsFromELF(path string) ([]elf.Symbol, error) {
// Load ELF executable
file, err := c.fs.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return err
return nil, err
}

elfFile, err := elf.NewFile(file)
if err != nil {
return err
return nil, err
}
defer elfFile.Close()

// Check imports based on symbols in the ELF file
symbols, err := elfFile.Symbols()
return elfFile.Symbols()
}

// checkUnsupportedImports checks whether the to-be-signed or to-be-executed binary uses Go imports which are not supported.
func (c *Cli) checkUnsupportedImports(path string) error {
symbols, err := c.getSymbolsFromELF(path)
if err != nil {
return fmt.Errorf("cannot read symbol table from given ELF binary: %w", err)
return fmt.Errorf("getting symbols: %w", err)
}
return checkUnsupportedImports(symbols)
}

func checkUnsupportedImports(symbols []elf.Symbol) error {
// Iterate through all symbols and find whether it matches a known unsupported one
for _, symbol := range symbols {
if strings.Contains(symbol.Name, "github.com/edgelesssys/ego/eclient") {
return ErrUnsupportedImportEClient
}
}
return nil
}

// checkHeapMode checks whether the heapSize is compatible with the binary.
// If it is built with the ego_largeheap build tag, heapSize must be >= 512.
// If it is built without this tag, heapSize must be <= 16384.
// (If 512 <= heapSize <= 16384, both modes work.)
func checkHeapMode(symbols []elf.Symbol, heapSize int) error {
for _, symbol := range symbols {
if symbol.Name == "runtime.arenaBaseOffset" {
// if this symbol is found, the binary wasn't built with ego_largeheap
if heapSize > 16384 {
return ErrNoLargeHeapWithLargeHeapSize
}
return nil
}
}
// if the symbol isn't found, the binary was built with ego_largeheap
if heapSize < 512 {
return ErrLargeHeapWithSmallHeapSize
}
return nil
}

Expand Down
53 changes: 53 additions & 0 deletions ego/cli/elf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package cli

import (
"debug/elf"
"encoding/json"
"io/ioutil"
"os"
Expand Down Expand Up @@ -149,3 +150,55 @@ func TestEmbedConfigAsPayload(t *testing.T) {
assert.NotEqualValues(jsonData, reconstructedJSON)
assert.EqualValues(jsonNewData, reconstructedJSON)
}

func TestCheckHeapMode(t *testing.T) {
testCases := map[string]struct {
symbols []elf.Symbol
heapSize int
want error
}{
"default heap, small": {
symbols: []elf.Symbol{{Name: "runtime.arenaBaseOffset"}},
heapSize: 511,
want: nil,
},
"default heap, lower bound": {
symbols: []elf.Symbol{{Name: "runtime.arenaBaseOffset"}},
heapSize: 512,
want: nil,
},
"default heap, upper bound": {
symbols: []elf.Symbol{{Name: "runtime.arenaBaseOffset"}},
heapSize: 16384,
want: nil,
},
"default heap, large": {
symbols: []elf.Symbol{{Name: "runtime.arenaBaseOffset"}},
heapSize: 16385,
want: ErrNoLargeHeapWithLargeHeapSize,
},
"large heap, small": {
heapSize: 511,
want: ErrLargeHeapWithSmallHeapSize,
},
"large heap, lower bound": {
heapSize: 512,
want: nil,
},
"large heap, upper bound": {
heapSize: 16384,
want: nil,
},
"large heap, large": {
heapSize: 16385,
want: nil,
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
assert.Equal(tc.want, checkHeapMode(tc.symbols, tc.heapSize))
})
}
}
12 changes: 11 additions & 1 deletion ego/cli/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,18 @@ var ErrNoOEInfo = errors.New("could not find .oeinfo section")
var errConfigDoesNotExist = errors.New("enclave config file not found")

func (c *Cli) signWithJSON(conf *config.Config) error {
symbols, err := c.getSymbolsFromELF(conf.Exe)
if err != nil {
return fmt.Errorf("getting symbols: %w", err)
}

// First, check if the executable does not contain unsupported imports / symbols.
if err := c.checkUnsupportedImports(conf.Exe); err != nil {
if err := checkUnsupportedImports(symbols); err != nil {
return err
}

// Check that heapSize is in the supported range of the heap mode the binary was built with.
if err := checkHeapMode(symbols, conf.HeapSize); err != nil {
return err
}

Expand Down
7 changes: 7 additions & 0 deletions ego/ego/cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ func handleErr(err error) {
fmt.Println("ERROR: You cannot import the github.com/edgelesssys/ego/eclient package within the EGo enclave.")
fmt.Println("It is intended to be used for applications running outside the SGX enclave.")
fmt.Println("You can use the github.com/edgelesssys/ego/enclave package as a replacement for usage inside the enclave.")
case cli.ErrLargeHeapWithSmallHeapSize:
fmt.Println("ERROR: The binary is built with build tag \"ego_largeheap\", but heapSize is set to less than 512.")
fmt.Println("Either increase heapSize or rebuild without this build tag.")
case cli.ErrNoLargeHeapWithLargeHeapSize:
fmt.Println("ERROR: The binary is built without build tag \"ego_largeheap\", but heapSize is set to more than 16384.")
fmt.Println("Either decrease heapSize or rebuild with this build tag:")
fmt.Println("\tego-go build -tags ego_largeheap ...")
default:
fmt.Println("ERROR:", err)
}
Expand Down
16 changes: 16 additions & 0 deletions src/integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ cd /tmp/ego-integration-test
run ego sign
run ego run integration-test

# Test heap size check on sign
sed -i 's/"heapSize": 16,/"heapSize": 16385,/' enclave.json
run ego sign |& grep "heapSize is set to more than"

# Test ego_largeheap
cd "$egoPath/ego/cmd/integration-test"
run ego-go build -o /tmp/ego-integration-test/integration-test -tags ego_largeheap
cd /tmp/ego-integration-test
run ego sign # sign with 16385 heapSize should succeed now
sed -i 's/"heapSize": 16385,/"heapSize": 511,/' enclave.json
run ego sign |& grep "heapSize is set to less than"
# Run integration test built with ego_largeheap and heapSize of 512 MB
sed -i 's/"heapSize": 511,/"heapSize": 512,/' enclave.json
run ego sign
run ego run integration-test

# Test unsupported import detection on sign & run
mkdir "$tPath/unsupported-import-test"
cd "$egoPath/ego/cmd/unsupported-import-test"
Expand Down

0 comments on commit d0ec57e

Please sign in to comment.