awnumar / memguard

Secure software enclave for storage of sensitive information in memory.
Apache License 2.0
2.49k stars 124 forks source link

Q: panic: <memcall> could not acquire lock #119

Closed ghost closed 4 years ago

ghost commented 4 years ago

Hi, I am just wasting some time so I thought I'd compare performance requirements and I am getting panic from enclave.Open(). Any idea why?

package whatever

import (
    "github.com/awnumar/memguard"
    "testing"
)

func BenchmarkEnclaveOpen(b *testing.B) {
    secret := []byte("secret secret secret")
    e := memguard.NewEnclave(secret)

    for i := 0; i < b.N; i++ {
        e.Open()
    }
}

func BenchmarkEnclaveOpenRead(b *testing.B) {
    secret := []byte("secret secret secret")
    e := memguard.NewEnclave(secret)

    for i := 0; i < b.N; i++ {
        lb, _ := e.Open()
        lb.String()
    }
}

func BenchmarkLBRead(b *testing.B) {
    secret := []byte("secret secret secret")
    lb := memguard.NewBufferFromBytes(secret)

    for i := 0; i < b.N; i++ {
        lb.String()
    }
}
awnumar commented 4 years ago

Can you post the trace? I'm not at a computer at the moment but I'll investigate as soon as I can.

ghost commented 4 years ago
$ go test -bench .
goos: windows
goarch: amd64
pkg: ...
BenchmarkEnclaveOpen-4                 0                      NaN ns/op
panic: <memcall> could not acquire lock on 0x2ce1000, limit reached? [Err: Insufficient quota to complete the requested service.]

goroutine 50 [running]:
github.com/awnumar/memguard/core.Panic(0x558020, 0xc000036bf0)
        .../go/src/github.com/awnumar/memguard/core/exit.go:73 +0x127
github.com/awnumar/memguard/core.NewBuffer(0x20, 0x1000, 0x2cd1000, 0xfec)
        .../go/src/github.com/awnumar/memguard/core/buffer.go:75 +0x342
github.com/awnumar/memguard/core.(*Coffer).View(0xc000099fb0, 0x0, 0x0, 0x0)
        .../go/src/github.com/awnumar/memguard/core/coffer.go:96 +0xc5
github.com/awnumar/memguard/core.Open(0xc000004040, 0xc000036bd0, 0x0, 0x0)
        .../go/src/github.com/awnumar/memguard/core/enclave.go:99 +0x67
github.com/awnumar/memguard.(*Enclave).Open(0xc000006008, 0xc000036bd0, 0x0, 0x0)
        .../go/src/github.com/awnumar/memguard/enclave.go:43 +0x39
....BenchmarkEnclaveOpen(0xc0000be1c0)
        ...bench_test.go:13 +0x7f
testing.(*B).runN(0xc0000be1c0, 0x64)
        C:/Go/src/testing/benchmark.go:190 +0xd3
testing.(*B).launch(0xc0000be1c0)
        C:/Go/src/testing/benchmark.go:320 +0x113
created by testing.(*B).doBench
        C:/Go/src/testing/benchmark.go:275 +0x5c
exit status 2
FAIL    ...  0.106s

It looks to me like the benchmarking does its stuff in parallel(though I don't know how) so it tries to access enclave from multiple goroutines at once....

awnumar commented 4 years ago

Try ulimit -l unlimited in the same shell before running the benchmarks.

ghost commented 4 years ago

Didn't help. But I am on a Win10 with git's bash so on *nix it could work.

awnumar commented 4 years ago

It looks like the process uses up it's allocated mlock limit. This is the amount of memory that the operating system allows each process to lock into volatile memory, preventing it from being swapped out to disk. I'm not sure how to increase this limit on Windows, but if you destroy the LockedBuffer objects that are created after creating them, this issue should disappear.

awnumar commented 4 years ago

Please let me know if that fixes the issue, and I'd be interested to see the results of the benchmarks as well alongside your system specs.

ghost commented 4 years ago

You are correct, this worked:

package whatever

import (
    "github.com/awnumar/memguard"
    "testing"
)

func BenchmarkEnclaveOpen(b *testing.B) {
    b.SetParallelism(1)
    secret := []byte("secret secret secret")
    e := memguard.NewEnclave(secret)

    for i := 0; i < b.N; i++ {
        lb, _ := e.Open()
        lb.Destroy()
    }
}

func BenchmarkEnclaveOpenRead(b *testing.B) {
    b.SetParallelism(1)
    secret := []byte("secret secret secret")
    e := memguard.NewEnclave(secret)

    for i := 0; i < b.N; i++ {
        lb, _ := e.Open()
        _ = lb.String()
        lb.Destroy()
    }
}

func BenchmarkLBRead(b *testing.B) {
    b.SetParallelism(1)
    secret := []byte("secret secret secret")
    lb := memguard.NewBufferFromBytes(secret)

    for i := 0; i < b.N; i++ {
        _ = lb.String()
        lb.Destroy()
    }
}

With results:

BenchmarkEnclaveOpen-4              4094            265510 ns/op
BenchmarkEnclaveOpenRead-4          4800            255416 ns/op
BenchmarkLBRead-4               16000704                70.4 ns/op

So it is very important to always destroy the LB. That is good to know :)

awnumar commented 4 years ago

Great. Destroying containers is critical. Please familiarise yourself with the documentation: https://godoc.org/github.com/awnumar/memguard