From e0683370c0f33726260d9de8a235680d1ab8a3ce Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 18:19:34 +0100 Subject: [PATCH 01/13] fix:add noallocate tests for hash and hmac --- hash_test.go | 39 +++++++++++++++++++++++++++++++++++++++ hmac_test.go | 21 +++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/hash_test.go b/hash_test.go index 16f0a0ba..f97cece5 100644 --- a/hash_test.go +++ b/hash_test.go @@ -382,6 +382,45 @@ func TestHashAllocations(t *testing.T) { } } +func TestHashStructAllocations(t *testing.T) { + if Asan() { + t.Skip("skipping allocations test with sanitizers") + } + msg := []byte("testing") + + md5Hash := openssl.NewMD5() + sha1Hash := openssl.NewSHA1() + sha256Hash := openssl.NewSHA256() + sha512Hash := openssl.NewSHA512() + + sum := make([]byte, sha512Hash.Size()) + n := int(testing.AllocsPerRun(10, func() { + md5Hash.Write(msg) + sha1Hash.Write(msg) + sha256Hash.Write(msg) + sha512Hash.Write(msg) + + md5Hash.Sum(sum[:0]) + sha1Hash.Sum(sum[:0]) + sha256Hash.Sum(sum[:0]) + sha512Hash.Sum(sum[:0]) + + md5Hash.Reset() + sha1Hash.Reset() + sha256Hash.Reset() + sha512Hash.Reset() + })) + want := 8 + if compareCurrentVersion("go1.24") >= 0 { + // The go1.24 compiler is able to optimize the allocation away. + // See cgo_go124.go for more information. + want = 0 + } + if n > want { + t.Errorf("allocs = %d, want %d", n, want) + } +} + func BenchmarkSHA256(b *testing.B) { b.StopTimer() h := openssl.NewSHA256() diff --git a/hmac_test.go b/hmac_test.go index 484ac9fd..53f870db 100644 --- a/hmac_test.go +++ b/hmac_test.go @@ -73,6 +73,27 @@ func TestHMACUnsupportedHash(t *testing.T) { } } +func TestHMACAllocations(t *testing.T) { + h := openssl.NewHMAC(openssl.NewSHA256, nil) + msg := []byte("hello world") + sum := make([]byte, openssl.NewSHA256().Size()) + n := int(testing.AllocsPerRun(10, func() { + h.Write(msg) + h.Sum(sum[:0]) + h.Reset() + })) + + want := 2 + if compareCurrentVersion("go1.24") >= 0 { + // The go1.24 compiler is able to optimize the allocation away. + // See cgo_go124.go for more information. + want = 0 + } + if n > want { + t.Errorf("allocs = %d, want %d", n, want) + } +} + func BenchmarkHMACSHA256_32(b *testing.B) { b.StopTimer() key := make([]byte, 32) From ebc3581f9968637a519b316eb4bf2e924936054f Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 18:39:26 +0100 Subject: [PATCH 02/13] comment out sum --- escapes.txt | 1 + hash_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 escapes.txt diff --git a/escapes.txt b/escapes.txt new file mode 100644 index 00000000..20d77cd1 --- /dev/null +++ b/escapes.txt @@ -0,0 +1 @@ +go: no Go source files diff --git a/hash_test.go b/hash_test.go index f97cece5..e743e3dc 100644 --- a/hash_test.go +++ b/hash_test.go @@ -393,17 +393,17 @@ func TestHashStructAllocations(t *testing.T) { sha256Hash := openssl.NewSHA256() sha512Hash := openssl.NewSHA512() - sum := make([]byte, sha512Hash.Size()) + // sum := make([]byte, sha512Hash.Size()) n := int(testing.AllocsPerRun(10, func() { md5Hash.Write(msg) sha1Hash.Write(msg) sha256Hash.Write(msg) sha512Hash.Write(msg) - md5Hash.Sum(sum[:0]) - sha1Hash.Sum(sum[:0]) - sha256Hash.Sum(sum[:0]) - sha512Hash.Sum(sum[:0]) + // md5Hash.Sum(sum[:0]) + // sha1Hash.Sum(sum[:0]) + // sha256Hash.Sum(sum[:0]) + // sha512Hash.Sum(sum[:0]) md5Hash.Reset() sha1Hash.Reset() From 091ecb386d590df7796273672678a1e8d2271c84 Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 18:43:13 +0100 Subject: [PATCH 03/13] fix:list go_hash_sum in noallocate and nocallback --- hash_test.go | 10 +++++----- internal/ossl/zossl_go124.go | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hash_test.go b/hash_test.go index e743e3dc..f97cece5 100644 --- a/hash_test.go +++ b/hash_test.go @@ -393,17 +393,17 @@ func TestHashStructAllocations(t *testing.T) { sha256Hash := openssl.NewSHA256() sha512Hash := openssl.NewSHA512() - // sum := make([]byte, sha512Hash.Size()) + sum := make([]byte, sha512Hash.Size()) n := int(testing.AllocsPerRun(10, func() { md5Hash.Write(msg) sha1Hash.Write(msg) sha256Hash.Write(msg) sha512Hash.Write(msg) - // md5Hash.Sum(sum[:0]) - // sha1Hash.Sum(sum[:0]) - // sha256Hash.Sum(sum[:0]) - // sha512Hash.Sum(sum[:0]) + md5Hash.Sum(sum[:0]) + sha1Hash.Sum(sum[:0]) + sha256Hash.Sum(sum[:0]) + sha512Hash.Sum(sum[:0]) md5Hash.Reset() sha1Hash.Reset() diff --git a/internal/ossl/zossl_go124.go b/internal/ossl/zossl_go124.go index 045bb380..8f9b331d 100644 --- a/internal/ossl/zossl_go124.go +++ b/internal/ossl/zossl_go124.go @@ -5,6 +5,8 @@ package ossl /* +#cgo noescape go_hash_sum +#cgo nocallback go_hash_sum #cgo noescape _mkcgo_EVP_CipherUpdate #cgo nocallback _mkcgo_EVP_CipherUpdate #cgo noescape _mkcgo_EVP_DecryptFinal_ex From 1129cbebb89e51372e52fa8283b42656185bbcfd Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 19:34:33 +0100 Subject: [PATCH 04/13] fix:make copy and final as escape --- internal/ossl/shims.h | 4 ++-- internal/ossl/zossl_go124.go | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/ossl/shims.h b/internal/ossl/shims.h index 9777c7cd..caf3fa1c 100644 --- a/internal/ossl/shims.h +++ b/internal/ossl/shims.h @@ -187,13 +187,13 @@ const _EVP_MD_PTR EVP_sha3_512(void) __attribute__((tag("111"),noerror)); _EVP_MD_CTX_PTR EVP_MD_CTX_new(void); void EVP_MD_CTX_free(_EVP_MD_CTX_PTR ctx); -int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in); +int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in) __attribute__((noescape,nocallback)); int EVP_MD_CTX_copy_ex(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in); int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const _EVP_MD_PTR type, _ENGINE_PTR impl) __attribute__((noescape,nocallback)); int EVP_DigestInit_ex(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type, _ENGINE_PTR impl); int EVP_DigestInit(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type); int EVP_DigestUpdate(_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt) __attribute__((noescape,nocallback)); -int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s); +int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s) __attribute__((noescape,nocallback)); int EVP_DigestSign(_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen) __attribute__((tag("111"),noescape,nocallback)); int EVP_DigestSignInit(_EVP_MD_CTX_PTR ctx, _EVP_PKEY_CTX_PTR *pctx, const _EVP_MD_PTR type, _ENGINE_PTR e, _EVP_PKEY_PTR pkey); int EVP_DigestSignFinal(_EVP_MD_CTX_PTR ctx, unsigned char *sig, size_t *siglen); diff --git a/internal/ossl/zossl_go124.go b/internal/ossl/zossl_go124.go index 8f9b331d..ae64d2f7 100644 --- a/internal/ossl/zossl_go124.go +++ b/internal/ossl/zossl_go124.go @@ -5,8 +5,6 @@ package ossl /* -#cgo noescape go_hash_sum -#cgo nocallback go_hash_sum #cgo noescape _mkcgo_EVP_CipherUpdate #cgo nocallback _mkcgo_EVP_CipherUpdate #cgo noescape _mkcgo_EVP_DecryptFinal_ex @@ -15,6 +13,8 @@ package ossl #cgo nocallback _mkcgo_EVP_DecryptUpdate #cgo noescape _mkcgo_EVP_Digest #cgo nocallback _mkcgo_EVP_Digest +#cgo noescape _mkcgo_EVP_DigestFinal_ex +#cgo nocallback _mkcgo_EVP_DigestFinal_ex #cgo noescape _mkcgo_EVP_DigestSign #cgo nocallback _mkcgo_EVP_DigestSign #cgo noescape _mkcgo_EVP_DigestUpdate @@ -23,6 +23,8 @@ package ossl #cgo nocallback _mkcgo_EVP_EncryptFinal_ex #cgo noescape _mkcgo_EVP_EncryptUpdate #cgo nocallback _mkcgo_EVP_EncryptUpdate +#cgo noescape _mkcgo_EVP_MD_CTX_copy +#cgo nocallback _mkcgo_EVP_MD_CTX_copy #cgo noescape _mkcgo_EVP_PKEY_derive #cgo nocallback _mkcgo_EVP_PKEY_derive #cgo noescape _mkcgo_EVP_PKEY_get_raw_private_key From 5e49bb662aff95484fd9e94a16c3316130294803 Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 19:37:52 +0100 Subject: [PATCH 05/13] fix:remove md5 --- hash_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hash_test.go b/hash_test.go index f97cece5..ff029c1d 100644 --- a/hash_test.go +++ b/hash_test.go @@ -388,29 +388,25 @@ func TestHashStructAllocations(t *testing.T) { } msg := []byte("testing") - md5Hash := openssl.NewMD5() sha1Hash := openssl.NewSHA1() sha256Hash := openssl.NewSHA256() sha512Hash := openssl.NewSHA512() sum := make([]byte, sha512Hash.Size()) n := int(testing.AllocsPerRun(10, func() { - md5Hash.Write(msg) sha1Hash.Write(msg) sha256Hash.Write(msg) sha512Hash.Write(msg) - md5Hash.Sum(sum[:0]) sha1Hash.Sum(sum[:0]) sha256Hash.Sum(sum[:0]) sha512Hash.Sum(sum[:0]) - md5Hash.Reset() sha1Hash.Reset() sha256Hash.Reset() sha512Hash.Reset() })) - want := 8 + want := 6 if compareCurrentVersion("go1.24") >= 0 { // The go1.24 compiler is able to optimize the allocation away. // See cgo_go124.go for more information. From fc1dfb3d5cdfcfe2eb39e68e4007f6a593c6e27d Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 19:45:36 +0100 Subject: [PATCH 06/13] fix:inline funccall --- hash.go | 10 ++++++++-- internal/ossl/shims.h | 4 ++-- internal/ossl/zossl_go124.go | 4 ---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hash.go b/hash.go index 9a275245..38841dd9 100644 --- a/hash.go +++ b/hash.go @@ -359,12 +359,18 @@ func (h *evpHash) BlockSize() int { func (h *evpHash) Sum(in []byte) []byte { h.init() + in = append(in, h.sum()...) + runtime.KeepAlive(h) + return in +} + +func (h *evpHash) sum() []byte { out := make([]byte, h.Size(), maxHashSize) // explicit cap to allow stack allocation if err := ossl.HashSum(h.ctx, h.ctx2, out); err != nil { panic(err) } - runtime.KeepAlive(h) - return append(in, out...) + + return out } // Clone returns a new evpHash object that is a deep clone of itself. diff --git a/internal/ossl/shims.h b/internal/ossl/shims.h index caf3fa1c..9777c7cd 100644 --- a/internal/ossl/shims.h +++ b/internal/ossl/shims.h @@ -187,13 +187,13 @@ const _EVP_MD_PTR EVP_sha3_512(void) __attribute__((tag("111"),noerror)); _EVP_MD_CTX_PTR EVP_MD_CTX_new(void); void EVP_MD_CTX_free(_EVP_MD_CTX_PTR ctx); -int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in) __attribute__((noescape,nocallback)); +int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in); int EVP_MD_CTX_copy_ex(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in); int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const _EVP_MD_PTR type, _ENGINE_PTR impl) __attribute__((noescape,nocallback)); int EVP_DigestInit_ex(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type, _ENGINE_PTR impl); int EVP_DigestInit(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type); int EVP_DigestUpdate(_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt) __attribute__((noescape,nocallback)); -int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s) __attribute__((noescape,nocallback)); +int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s); int EVP_DigestSign(_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen) __attribute__((tag("111"),noescape,nocallback)); int EVP_DigestSignInit(_EVP_MD_CTX_PTR ctx, _EVP_PKEY_CTX_PTR *pctx, const _EVP_MD_PTR type, _ENGINE_PTR e, _EVP_PKEY_PTR pkey); int EVP_DigestSignFinal(_EVP_MD_CTX_PTR ctx, unsigned char *sig, size_t *siglen); diff --git a/internal/ossl/zossl_go124.go b/internal/ossl/zossl_go124.go index ae64d2f7..045bb380 100644 --- a/internal/ossl/zossl_go124.go +++ b/internal/ossl/zossl_go124.go @@ -13,8 +13,6 @@ package ossl #cgo nocallback _mkcgo_EVP_DecryptUpdate #cgo noescape _mkcgo_EVP_Digest #cgo nocallback _mkcgo_EVP_Digest -#cgo noescape _mkcgo_EVP_DigestFinal_ex -#cgo nocallback _mkcgo_EVP_DigestFinal_ex #cgo noescape _mkcgo_EVP_DigestSign #cgo nocallback _mkcgo_EVP_DigestSign #cgo noescape _mkcgo_EVP_DigestUpdate @@ -23,8 +21,6 @@ package ossl #cgo nocallback _mkcgo_EVP_EncryptFinal_ex #cgo noescape _mkcgo_EVP_EncryptUpdate #cgo nocallback _mkcgo_EVP_EncryptUpdate -#cgo noescape _mkcgo_EVP_MD_CTX_copy -#cgo nocallback _mkcgo_EVP_MD_CTX_copy #cgo noescape _mkcgo_EVP_PKEY_derive #cgo nocallback _mkcgo_EVP_PKEY_derive #cgo noescape _mkcgo_EVP_PKEY_get_raw_private_key From ae5d811b1fd758abdc86d332bab9166fbe95800b Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 20:07:32 +0100 Subject: [PATCH 07/13] Fix:use cgo calls --- escapes.txt | 1 - hash_test.go | 4 ++++ internal/ossl/ossl.go | 30 ++++++------------------------ internal/ossl/shims.h | 4 ++-- internal/ossl/zossl_go124.go | 4 ++++ 5 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 escapes.txt diff --git a/escapes.txt b/escapes.txt deleted file mode 100644 index 20d77cd1..00000000 --- a/escapes.txt +++ /dev/null @@ -1 +0,0 @@ -go: no Go source files diff --git a/hash_test.go b/hash_test.go index ff029c1d..dccba0ca 100644 --- a/hash_test.go +++ b/hash_test.go @@ -389,20 +389,24 @@ func TestHashStructAllocations(t *testing.T) { msg := []byte("testing") sha1Hash := openssl.NewSHA1() + sha224Hash := openssl.NewSHA1() sha256Hash := openssl.NewSHA256() sha512Hash := openssl.NewSHA512() sum := make([]byte, sha512Hash.Size()) n := int(testing.AllocsPerRun(10, func() { sha1Hash.Write(msg) + sha224Hash.Write(msg) sha256Hash.Write(msg) sha512Hash.Write(msg) sha1Hash.Sum(sum[:0]) + sha224Hash.Sum(sum[:0]) sha256Hash.Sum(sum[:0]) sha512Hash.Sum(sum[:0]) sha1Hash.Reset() + sha224Hash.Reset() sha256Hash.Reset() sha512Hash.Reset() })) diff --git a/internal/ossl/ossl.go b/internal/ossl/ossl.go index 4a64c2f0..4534e754 100644 --- a/internal/ossl/ossl.go +++ b/internal/ossl/ossl.go @@ -5,36 +5,18 @@ package ossl /* #include "zossl.h" -// go_hash_sum copies ctx into ctx2 and calls EVP_DigestFinal_ex using ctx2. -// This is necessary because Go hash.Hash mandates that Sum has no effect -// on the underlying stream. In particular it is OK to Sum, then Write more, -// then Sum again, and the second Sum acts as if the first didn't happen. -// It is written in C because Sum() tend to be in the hot path, -// and doing one cgo call instead of two is a significant performance win. -static inline int -go_hash_sum(const _EVP_MD_CTX_PTR ctx, _EVP_MD_CTX_PTR ctx2, unsigned char *out, mkcgo_err_state *_err_state) -{ - if (_mkcgo_EVP_MD_CTX_copy(ctx2, ctx, _err_state) != 1) - return -1; - if (_mkcgo_EVP_DigestFinal_ex(ctx2, out, NULL, _err_state) <= 0) - return -2; - return 1; -} */ import "C" import "unsafe" func HashSum(ctx1, ctx2 EVP_MD_CTX_PTR, out []byte) error { var errst C.mkcgo_err_state - if code := C.go_hash_sum(ctx1, ctx2, (*C.uchar)(unsafe.SliceData(out)), mkcgoNoEscape(&errst)); code != 1 { - msg := "go_hash_sum" - switch code { - case -1: - msg = "EVP_MD_CTX_copy" - case -2: - msg = "EVP_DigestFinal_ex" - } - return newMkcgoErr(msg, errst) + if C._mkcgo_EVP_MD_CTX_copy(ctx2, ctx1, mkcgoNoEscape(&errst)) != 1 { + return newMkcgoErr("EVP_MD_CTX_copy", errst) + } + if C._mkcgo_EVP_DigestFinal_ex(ctx2, (*C.uchar)(unsafe.SliceData(out)), nil, mkcgoNoEscape(&errst)) <= 0 { + return newMkcgoErr("EVP_DigestFinal_ex", errst) } + return nil } diff --git a/internal/ossl/shims.h b/internal/ossl/shims.h index 9777c7cd..caf3fa1c 100644 --- a/internal/ossl/shims.h +++ b/internal/ossl/shims.h @@ -187,13 +187,13 @@ const _EVP_MD_PTR EVP_sha3_512(void) __attribute__((tag("111"),noerror)); _EVP_MD_CTX_PTR EVP_MD_CTX_new(void); void EVP_MD_CTX_free(_EVP_MD_CTX_PTR ctx); -int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in); +int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in) __attribute__((noescape,nocallback)); int EVP_MD_CTX_copy_ex(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in); int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const _EVP_MD_PTR type, _ENGINE_PTR impl) __attribute__((noescape,nocallback)); int EVP_DigestInit_ex(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type, _ENGINE_PTR impl); int EVP_DigestInit(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type); int EVP_DigestUpdate(_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt) __attribute__((noescape,nocallback)); -int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s); +int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s) __attribute__((noescape,nocallback)); int EVP_DigestSign(_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen) __attribute__((tag("111"),noescape,nocallback)); int EVP_DigestSignInit(_EVP_MD_CTX_PTR ctx, _EVP_PKEY_CTX_PTR *pctx, const _EVP_MD_PTR type, _ENGINE_PTR e, _EVP_PKEY_PTR pkey); int EVP_DigestSignFinal(_EVP_MD_CTX_PTR ctx, unsigned char *sig, size_t *siglen); diff --git a/internal/ossl/zossl_go124.go b/internal/ossl/zossl_go124.go index 045bb380..ae64d2f7 100644 --- a/internal/ossl/zossl_go124.go +++ b/internal/ossl/zossl_go124.go @@ -13,6 +13,8 @@ package ossl #cgo nocallback _mkcgo_EVP_DecryptUpdate #cgo noescape _mkcgo_EVP_Digest #cgo nocallback _mkcgo_EVP_Digest +#cgo noescape _mkcgo_EVP_DigestFinal_ex +#cgo nocallback _mkcgo_EVP_DigestFinal_ex #cgo noescape _mkcgo_EVP_DigestSign #cgo nocallback _mkcgo_EVP_DigestSign #cgo noescape _mkcgo_EVP_DigestUpdate @@ -21,6 +23,8 @@ package ossl #cgo nocallback _mkcgo_EVP_EncryptFinal_ex #cgo noescape _mkcgo_EVP_EncryptUpdate #cgo nocallback _mkcgo_EVP_EncryptUpdate +#cgo noescape _mkcgo_EVP_MD_CTX_copy +#cgo nocallback _mkcgo_EVP_MD_CTX_copy #cgo noescape _mkcgo_EVP_PKEY_derive #cgo nocallback _mkcgo_EVP_PKEY_derive #cgo noescape _mkcgo_EVP_PKEY_get_raw_private_key From be2b443177796265ab08066d6ad14fead530c8c4 Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 20:28:57 +0100 Subject: [PATCH 08/13] fix:use hashfield as sum --- hash.go | 15 ++++++--------- hash_test.go | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hash.go b/hash.go index 38841dd9..22e46c1e 100644 --- a/hash.go +++ b/hash.go @@ -255,6 +255,8 @@ type evpHash struct { // same allocated object multiple times. ctx2 ossl.EVP_MD_CTX_PTR pinner runtime.Pinner + + out [maxHashSize]byte } func newEvpHash(ch crypto.Hash) *evpHash { @@ -359,18 +361,13 @@ func (h *evpHash) BlockSize() int { func (h *evpHash) Sum(in []byte) []byte { h.init() - in = append(in, h.sum()...) - runtime.KeepAlive(h) - return in -} - -func (h *evpHash) sum() []byte { - out := make([]byte, h.Size(), maxHashSize) // explicit cap to allow stack allocation - if err := ossl.HashSum(h.ctx, h.ctx2, out); err != nil { + tmp := h.out[:h.Size()] // Create slice view + if err := ossl.HashSum(h.ctx, h.ctx2, tmp); err != nil { panic(err) } + runtime.KeepAlive(h) - return out + return append(in, tmp...) } // Clone returns a new evpHash object that is a deep clone of itself. diff --git a/hash_test.go b/hash_test.go index dccba0ca..63d82e8f 100644 --- a/hash_test.go +++ b/hash_test.go @@ -410,7 +410,7 @@ func TestHashStructAllocations(t *testing.T) { sha256Hash.Reset() sha512Hash.Reset() })) - want := 6 + want := 4 if compareCurrentVersion("go1.24") >= 0 { // The go1.24 compiler is able to optimize the allocation away. // See cgo_go124.go for more information. From cfb63ed20d3bb5dfdf2d174a0f1d30f1f0bbffb4 Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 20:33:32 +0100 Subject: [PATCH 09/13] revert:hashshum --- internal/ossl/ossl.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/internal/ossl/ossl.go b/internal/ossl/ossl.go index 4534e754..4a64c2f0 100644 --- a/internal/ossl/ossl.go +++ b/internal/ossl/ossl.go @@ -5,18 +5,36 @@ package ossl /* #include "zossl.h" +// go_hash_sum copies ctx into ctx2 and calls EVP_DigestFinal_ex using ctx2. +// This is necessary because Go hash.Hash mandates that Sum has no effect +// on the underlying stream. In particular it is OK to Sum, then Write more, +// then Sum again, and the second Sum acts as if the first didn't happen. +// It is written in C because Sum() tend to be in the hot path, +// and doing one cgo call instead of two is a significant performance win. +static inline int +go_hash_sum(const _EVP_MD_CTX_PTR ctx, _EVP_MD_CTX_PTR ctx2, unsigned char *out, mkcgo_err_state *_err_state) +{ + if (_mkcgo_EVP_MD_CTX_copy(ctx2, ctx, _err_state) != 1) + return -1; + if (_mkcgo_EVP_DigestFinal_ex(ctx2, out, NULL, _err_state) <= 0) + return -2; + return 1; +} */ import "C" import "unsafe" func HashSum(ctx1, ctx2 EVP_MD_CTX_PTR, out []byte) error { var errst C.mkcgo_err_state - if C._mkcgo_EVP_MD_CTX_copy(ctx2, ctx1, mkcgoNoEscape(&errst)) != 1 { - return newMkcgoErr("EVP_MD_CTX_copy", errst) - } - if C._mkcgo_EVP_DigestFinal_ex(ctx2, (*C.uchar)(unsafe.SliceData(out)), nil, mkcgoNoEscape(&errst)) <= 0 { - return newMkcgoErr("EVP_DigestFinal_ex", errst) + if code := C.go_hash_sum(ctx1, ctx2, (*C.uchar)(unsafe.SliceData(out)), mkcgoNoEscape(&errst)); code != 1 { + msg := "go_hash_sum" + switch code { + case -1: + msg = "EVP_MD_CTX_copy" + case -2: + msg = "EVP_DigestFinal_ex" + } + return newMkcgoErr(msg, errst) } - return nil } From 5d819f838084dcb756e622374ef665e3416207ff Mon Sep 17 00:00:00 2001 From: mertakman Date: Tue, 15 Apr 2025 20:49:36 +0100 Subject: [PATCH 10/13] fix:make hmac zeroalloc --- hmac.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/hmac.go b/hmac.go index 35cbc289..7a74405a 100644 --- a/hmac.go +++ b/hmac.go @@ -73,7 +73,7 @@ type opensslHMAC struct { ctx3 hmacCtx3 size int blockSize int - sum []byte + sum [maxHashSize]byte } func newHMAC1(key []byte, md ossl.EVP_MD_PTR) hmacCtx1 { @@ -180,7 +180,7 @@ func (h *opensslHMAC) Reset() { } runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure. - h.sum = nil + h.sum = [maxHashSize]byte{} } func (h *opensslHMAC) finalize() { @@ -216,10 +216,6 @@ func (h *opensslHMAC) BlockSize() int { } func (h *opensslHMAC) Sum(in []byte) []byte { - if h.sum == nil { - size := h.Size() - h.sum = make([]byte, size) - } // Make copy of context because Go hash.Hash mandates // that Sum has no effect on the underlying stream. // In particular it is OK to Sum, then Write more, then Sum again, @@ -234,16 +230,16 @@ func (h *opensslHMAC) Sum(in []byte) []byte { if _, err := ossl.HMAC_CTX_copy(ctx2, h.ctx1.ctx); err != nil { panic(err) } - ossl.HMAC_Final(ctx2, base(h.sum), nil) + ossl.HMAC_Final(ctx2, base(h.sum[:h.size]), nil) case 3: ctx2, err := ossl.EVP_MAC_CTX_dup(h.ctx3.ctx) if err != nil { panic(err) } defer ossl.EVP_MAC_CTX_free(ctx2) - ossl.EVP_MAC_final(ctx2, base(h.sum), nil, len(h.sum)) + ossl.EVP_MAC_final(ctx2, base(h.sum[:h.size]), nil, len(h.sum)) default: panic(errUnsupportedVersion()) } - return append(in, h.sum...) + return append(in, h.sum[:h.size]...) } From f7d364019fcfd2e813f3df048be9d9f7a9a489ef Mon Sep 17 00:00:00 2001 From: mertakman Date: Thu, 17 Apr 2025 15:36:53 +0100 Subject: [PATCH 11/13] fix:address cr's --- hash.go | 1 + hmac.go | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hash.go b/hash.go index 22e46c1e..acef52ac 100644 --- a/hash.go +++ b/hash.go @@ -362,6 +362,7 @@ func (h *evpHash) BlockSize() int { func (h *evpHash) Sum(in []byte) []byte { h.init() tmp := h.out[:h.Size()] // Create slice view + clear(tmp) if err := ossl.HashSum(h.ctx, h.ctx2, tmp); err != nil { panic(err) } diff --git a/hmac.go b/hmac.go index 7a74405a..a6bb884d 100644 --- a/hmac.go +++ b/hmac.go @@ -180,7 +180,6 @@ func (h *opensslHMAC) Reset() { } runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure. - h.sum = [maxHashSize]byte{} } func (h *opensslHMAC) finalize() { From f75dfc54f3c41c2c0e412eec4a8ceb2d7af911ea Mon Sep 17 00:00:00 2001 From: mertakman Date: Thu, 17 Apr 2025 18:57:33 +0100 Subject: [PATCH 12/13] fix:rm empty line --- hash.go | 1 - 1 file changed, 1 deletion(-) diff --git a/hash.go b/hash.go index acef52ac..c26005a6 100644 --- a/hash.go +++ b/hash.go @@ -367,7 +367,6 @@ func (h *evpHash) Sum(in []byte) []byte { panic(err) } runtime.KeepAlive(h) - return append(in, tmp...) } From e56ceab69074aee2d5f79e6b9dc7dbc01998486b Mon Sep 17 00:00:00 2001 From: mertakman Date: Wed, 23 Apr 2025 16:39:48 +0100 Subject: [PATCH 13/13] fix:remove forgotten code --- hash.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hash.go b/hash.go index e487354c..033169be 100644 --- a/hash.go +++ b/hash.go @@ -248,8 +248,8 @@ type evpHash struct { // ctx2 is used in evpHash.sum to avoid changing // the state of ctx. Having it here allows reusing the // same allocated object multiple times. - ctx2 ossl.EVP_MD_CTX_PTR - out [maxHashSize]byte + ctx2 ossl.EVP_MD_CTX_PTR + out [maxHashSize]byte } func newEvpHash(ch crypto.Hash) *evpHash { @@ -325,7 +325,7 @@ func (h *evpHash) WriteString(s string) (int, error) { if len(s) == 0 { return 0, nil } -ner h.init() + h.init() if _, err := ossl.EVP_DigestUpdate(h.ctx, unsafe.Pointer(unsafe.StringData(s)), len(s)); err != nil { panic(err) }