From 9294d42d63107a98c67b53d935a4a64915573aea Mon Sep 17 00:00:00 2001 From: Thomas Tendyck Date: Mon, 25 Sep 2023 16:49:10 +0200 Subject: [PATCH] add ego_largeheap build tag as workaround for heap sizes larger than 16GB --- _ertgo | 2 +- ego/cli/elf.go | 46 +++++++++++++++++++++++++++++------ ego/cli/elf_test.go | 53 +++++++++++++++++++++++++++++++++++++++++ ego/cli/sign.go | 12 +++++++++- ego/ego/cmd/util.go | 7 ++++++ src/integration_test.sh | 16 +++++++++++++ 6 files changed, 127 insertions(+), 9 deletions(-) diff --git a/_ertgo b/_ertgo index ba1d006c..4950e9b2 160000 --- a/_ertgo +++ b/_ertgo @@ -1 +1 @@ -Subproject commit ba1d006c53570a22b727a1617ec688e9d767ea2f +Subproject commit 4950e9b24c673b4a98d1a92d4819fb2c4cdbf5b0 diff --git a/ego/cli/elf.go b/ego/cli/elf.go index 61baac9c..40aab9b7 100644 --- a/ego/cli/elf.go +++ b/ego/cli/elf.go @@ -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) @@ -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 } diff --git a/ego/cli/elf_test.go b/ego/cli/elf_test.go index 32dbf1e3..021f0979 100644 --- a/ego/cli/elf_test.go +++ b/ego/cli/elf_test.go @@ -7,6 +7,7 @@ package cli import ( + "debug/elf" "encoding/json" "io/ioutil" "os" @@ -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)) + }) + } +} diff --git a/ego/cli/sign.go b/ego/cli/sign.go index bb443225..bd9ab4dc 100644 --- a/ego/cli/sign.go +++ b/ego/cli/sign.go @@ -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 } diff --git a/ego/ego/cmd/util.go b/ego/ego/cmd/util.go index 070f213e..311e2f35 100644 --- a/ego/ego/cmd/util.go +++ b/ego/ego/cmd/util.go @@ -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) } diff --git a/src/integration_test.sh b/src/integration_test.sh index 214edc87..f25c73c1 100755 --- a/src/integration_test.sh +++ b/src/integration_test.sh @@ -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"