From 508780bb257e7d8eb2f0db9196c26e374bf36f52 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Thu, 11 Sep 2025 15:07:21 +0100 Subject: [PATCH] fix init deadlock --- buffer.go | 4 ++-- core/buffer.go | 20 ++++++++++---------- core/buffer_test.go | 14 +++++++------- core/coffer.go | 10 +++++----- core/coffer_test.go | 12 ++++++------ core/crypto.go | 2 +- core/enclave.go | 8 ++++---- core/enclave_test.go | 2 +- core/exit.go | 12 +++++++----- core/exit_test.go | 10 +++++----- enclave.go | 4 ++-- memguard.go | 6 +++--- 12 files changed, 53 insertions(+), 51 deletions(-) diff --git a/buffer.go b/buffer.go index 566704d4..522e25a5 100644 --- a/buffer.go +++ b/buffer.go @@ -33,7 +33,7 @@ NewBuffer creates a mutable data container of the specified size. */ func NewBuffer(size int) *LockedBuffer { // Construct a Buffer of the specified size. - buf, err := core.NewBuffer(size) + buf, err := core.NewBuffer(size, false) if err != nil { return newNullBuffer() } @@ -248,7 +248,7 @@ func (b *LockedBuffer) Seal() *Enclave { if err == core.ErrBufferExpired { return nil } - core.Panic(err) + core.Panic(err, false) } return &Enclave{e} } diff --git a/core/buffer.go b/core/buffer.go index fd100299..58c59872 100644 --- a/core/buffer.go +++ b/core/buffer.go @@ -42,7 +42,7 @@ type Buffer struct { /* NewBuffer is a raw constructor for the Buffer object. */ -func NewBuffer(size int) (*Buffer, error) { +func NewBuffer(size int, keyMtxLocked bool) (*Buffer, error) { var err error if size < 1 { @@ -55,7 +55,7 @@ func NewBuffer(size int) (*Buffer, error) { innerLen := roundToPageSize(size) b.memory, err = memcall.Alloc((2 * pageSize) + innerLen) if err != nil { - Panic(err) + Panic(err, keyMtxLocked) } // Construct slice reference for data buffer. @@ -71,22 +71,22 @@ func NewBuffer(size int) (*Buffer, error) { // Lock the pages that will hold sensitive data. if err := memcall.Lock(b.inner); err != nil { - Panic(err) + Panic(err, keyMtxLocked) } // Initialise the canary value and reference regions. if err := Scramble(b.canary); err != nil { - Panic(err) + Panic(err, keyMtxLocked) } Copy(b.preguard, b.canary) Copy(b.postguard, b.canary) // Make the guard pages inaccessible. if err := memcall.Protect(b.preguard, memcall.NoAccess()); err != nil { - Panic(err) + Panic(err, keyMtxLocked) } if err := memcall.Protect(b.postguard, memcall.NoAccess()); err != nil { - Panic(err) + Panic(err, keyMtxLocked) } // Set remaining properties @@ -113,7 +113,7 @@ func (b *Buffer) Inner() []byte { // Freeze makes the underlying memory of a given buffer immutable. This will do nothing if the Buffer has been destroyed. func (b *Buffer) Freeze() { if err := b.freeze(); err != nil { - Panic(err) + Panic(err, false) } } @@ -138,7 +138,7 @@ func (b *Buffer) freeze() error { // Melt makes the underlying memory of a given buffer mutable. This will do nothing if the Buffer has been destroyed. func (b *Buffer) Melt() { if err := b.melt(); err != nil { - Panic(err) + Panic(err, false) } } @@ -162,7 +162,7 @@ func (b *Buffer) melt() error { // Scramble attempts to overwrite the data with cryptographically-secure random bytes. func (b *Buffer) Scramble() { if err := b.scramble(); err != nil { - Panic(err) + Panic(err, false) } } @@ -179,7 +179,7 @@ If the Buffer has already been destroyed, the function does nothing and returns */ func (b *Buffer) Destroy() { if err := b.destroy(); err != nil { - Panic(err) + Panic(err, false) } // Remove this one from global slice. buffers.remove(b) diff --git a/core/buffer_test.go b/core/buffer_test.go index 175d7f1b..9b3284dd 100644 --- a/core/buffer_test.go +++ b/core/buffer_test.go @@ -8,7 +8,7 @@ import ( func TestNewBuffer(t *testing.T) { // Check the error case with zero length. - b, err := NewBuffer(0) + b, err := NewBuffer(0, false) if err != ErrNullBuffer { t.Error("expected ErrNullBuffer; got", err) } @@ -17,7 +17,7 @@ func TestNewBuffer(t *testing.T) { } // Check the error case with negative length. - b, err = NewBuffer(-1) + b, err = NewBuffer(-1, false) if err != ErrNullBuffer { t.Error("expected ErrNullBuffer; got", err) } @@ -26,7 +26,7 @@ func TestNewBuffer(t *testing.T) { } // Test normal execution. - b, err = NewBuffer(32) + b, err = NewBuffer(32, false) if err != nil { t.Error("expected nil err; got", err) } @@ -57,7 +57,7 @@ func TestNewBuffer(t *testing.T) { func TestLotsOfAllocs(t *testing.T) { for i := 1; i <= 16385; i++ { - b, err := NewBuffer(i) + b, err := NewBuffer(i, false) if err != nil { t.Error(err) } @@ -92,7 +92,7 @@ func TestLotsOfAllocs(t *testing.T) { } func TestData(t *testing.T) { - b, err := NewBuffer(32) + b, err := NewBuffer(32, false) if err != nil { t.Error(err) } @@ -126,7 +126,7 @@ func TestData(t *testing.T) { } func TestBufferState(t *testing.T) { - b, err := NewBuffer(32) + b, err := NewBuffer(32, false) if err != nil { t.Error("expected nil err; got", err) } @@ -172,7 +172,7 @@ func TestBufferState(t *testing.T) { func TestDestroy(t *testing.T) { // Allocate a new buffer. - b, err := NewBuffer(32) + b, err := NewBuffer(32, false) if err != nil { t.Error("expected nil err; got", err) } diff --git a/core/coffer.go b/core/coffer.go index 1efdffaf..e73a2a81 100644 --- a/core/coffer.go +++ b/core/coffer.go @@ -25,11 +25,11 @@ type Coffer struct { } // NewCoffer is a raw constructor for the *Coffer object. -func NewCoffer() *Coffer { +func NewCoffer(keyMtxLocked bool) *Coffer { s := new(Coffer) - s.left, _ = NewBuffer(32) - s.right, _ = NewBuffer(32) - s.rand, _ = NewBuffer(32) + s.left, _ = NewBuffer(32, keyMtxLocked) + s.right, _ = NewBuffer(32, keyMtxLocked) + s.rand, _ = NewBuffer(32, keyMtxLocked) s.Init() @@ -80,7 +80,7 @@ func (s *Coffer) View() (*Buffer, error) { if s.destroyed() { return nil, ErrCofferExpired } - b, _ := NewBuffer(32) + b, _ := NewBuffer(32, false) // data = hash(right) XOR left h := Hash(s.right.Data()) diff --git a/core/coffer_test.go b/core/coffer_test.go index 297365ae..8e08abcd 100644 --- a/core/coffer_test.go +++ b/core/coffer_test.go @@ -9,7 +9,7 @@ import ( ) func TestNewCoffer(t *testing.T) { - s := NewCoffer() + s := NewCoffer(false) // Attain a lock to halt the verify & rekey cycle. s.Lock() @@ -40,7 +40,7 @@ func TestNewCoffer(t *testing.T) { } func TestCofferInit(t *testing.T) { - s := NewCoffer() + s := NewCoffer(false) // Get the value stored inside. view, err := s.View() @@ -79,7 +79,7 @@ func TestCofferInit(t *testing.T) { } func TestCofferView(t *testing.T) { - s := NewCoffer() + s := NewCoffer(false) // Get the value stored inside. view, err := s.View() @@ -114,7 +114,7 @@ func TestCofferView(t *testing.T) { } func TestCofferRekey(t *testing.T) { - s := NewCoffer() + s := NewCoffer(false) // remember the value stored inside view, err := s.View() @@ -159,7 +159,7 @@ func TestCofferRekey(t *testing.T) { } func TestCofferDestroy(t *testing.T) { - s := NewCoffer() + s := NewCoffer(false) s.Destroy() // Check metadata flags. @@ -188,7 +188,7 @@ func TestCofferConcurrent(t *testing.T) { } wg := &sync.WaitGroup{} - s := NewCoffer() + s := NewCoffer(false) defer s.Destroy() start := time.Now() diff --git a/core/crypto.go b/core/crypto.go index 0a260bc7..14889e24 100644 --- a/core/crypto.go +++ b/core/crypto.go @@ -36,7 +36,7 @@ func Encrypt(plaintext, key []byte) ([]byte, error) { // Allocate space for and generate a nonce value. var nonce [24]byte if err := Scramble(nonce[:]); err != nil { - Panic(err) + Panic(err, false) } // Encrypt m and return the result. diff --git a/core/enclave.go b/core/enclave.go index 28ec2118..80cb5ec5 100644 --- a/core/enclave.go +++ b/core/enclave.go @@ -15,7 +15,7 @@ func getOrCreateKey() *Coffer { defer keyMtx.Unlock() if key.Destroyed() { - key = NewCoffer() + key = NewCoffer(true) } return key @@ -59,7 +59,7 @@ func NewEnclave(buf []byte) (*Enclave, error) { // Encrypt the plaintext. e.ciphertext, err = Encrypt(buf, k.Data()) if err != nil { - Panic(err) // key is not 32 bytes long + Panic(err, false) // key is not 32 bytes long } // Destroy our copy of the key. @@ -106,9 +106,9 @@ The Buffer object should be destroyed after the contents are no longer needed. */ func Open(e *Enclave) (*Buffer, error) { // Allocate a secure Buffer to hold the decrypted data. - b, err := NewBuffer(len(e.ciphertext) - Overhead) + b, err := NewBuffer(len(e.ciphertext)-Overhead, false) if err != nil { - Panic(" ciphertext has invalid length") // ciphertext has invalid length + Panic(" ciphertext has invalid length", false) // ciphertext has invalid length } // Grab a view of the key. diff --git a/core/enclave_test.go b/core/enclave_test.go index 6a9f4059..5bb386c4 100644 --- a/core/enclave_test.go +++ b/core/enclave_test.go @@ -35,7 +35,7 @@ func TestNewEnclave(t *testing.T) { func TestSeal(t *testing.T) { // Create a new buffer for testing with. - b, err := NewBuffer(32) + b, err := NewBuffer(32, false) if err != nil { t.Error(err) } diff --git a/core/exit.go b/core/exit.go index eb29ef16..11c22797 100644 --- a/core/exit.go +++ b/core/exit.go @@ -14,13 +14,15 @@ The creation of new Enclave objects should wait for this function to return sinc This function should be called before the program terminates, or else the provided Exit or Panic functions should be used to terminate. */ -func Purge() { +func Purge(keyMtxLocked bool) { var opErr error func() { // Halt the re-key cycle and prevent new enclaves or keys being created. - keyMtx.Lock() - defer keyMtx.Unlock() + if !keyMtxLocked { + keyMtx.Lock() + defer keyMtx.Unlock() + } if !key.Destroyed() { key.Lock() defer key.Unlock() @@ -81,7 +83,7 @@ func Exit(c int) { /* Panic is identical to the builtin panic except it purges the session before calling panic. */ -func Panic(v interface{}) { - Purge() // creates a new key so it is safe to recover from this panic +func Panic(v interface{}, keyMtxLocked bool) { + Purge(keyMtxLocked) // creates a new key so it is safe to recover from this panic panic(v) } diff --git a/core/exit_test.go b/core/exit_test.go index 90828722..310a3804 100644 --- a/core/exit_test.go +++ b/core/exit_test.go @@ -11,13 +11,13 @@ func TestPurge(t *testing.T) { if err != nil { t.Error(err) } - buffer, err := NewBuffer(32) + buffer, err := NewBuffer(32, false) if err != nil { t.Error(err) } oldKey := getOrCreateKey() - Purge() + Purge(false) key := getOrCreateKey() // Verify that the buffers list contains only the important buffers. @@ -56,14 +56,14 @@ func TestPurge(t *testing.T) { } // Create a buffer with invalid canary. - b, err := NewBuffer(32) + b, err := NewBuffer(32, false) if err != nil { t.Error(err) } Scramble(b.inner) b.Freeze() if !panics(func() { - Purge() + Purge(false) }) { t.Error("did not panic") } @@ -76,7 +76,7 @@ func TestPurge(t *testing.T) { func TestPanic(t *testing.T) { // Call Panic and check if it panics. if !panics(func() { - Panic("test") + Panic("test", false) }) { t.Error("did not panic") } diff --git a/enclave.go b/enclave.go index 91c7732b..be60c227 100644 --- a/enclave.go +++ b/enclave.go @@ -22,7 +22,7 @@ func NewEnclave(src []byte) *Enclave { if err == core.ErrNullEnclave { return nil } - core.Panic(err) + core.Panic(err, false) } return &Enclave{e} } @@ -43,7 +43,7 @@ func (e *Enclave) Open() (*LockedBuffer, error) { b, err := core.Open(e.Enclave) if err != nil { if err != core.ErrDecryptionFailed { - core.Panic(err) + core.Panic(err, false) } return nil, err } diff --git a/memguard.go b/memguard.go index 32182194..13dca455 100644 --- a/memguard.go +++ b/memguard.go @@ -11,7 +11,7 @@ ScrambleBytes overwrites an arbitrary buffer with cryptographically-secure rando */ func ScrambleBytes(buf []byte) { if err := core.Scramble(buf); err != nil { - core.Panic(err) + core.Panic(err, false) } } @@ -26,14 +26,14 @@ func WipeBytes(buf []byte) { Purge resets the session key to a fresh value and destroys all existing LockedBuffers. Existing Enclave objects will no longer be decryptable. */ func Purge() { - core.Purge() + core.Purge(false) } /* SafePanic wipes all it can before calling panic(v). */ func SafePanic(v interface{}) { - core.Panic(v) + core.Panic(v, false) } /*