Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ego_largeheap build tag as workaround for heap sizes larger than 16GB #227

Merged
merged 1 commit into from
Sep 26, 2023
Merged
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
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
Loading