-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Free thread-local OpenSSL state on thread exit
OpenSSL 1.0.x automatically allocates a per-thread error queue as needed, but requires that the application call ERR_remove_thread_state() when terminating a thread to prevent the memory from leaking. As the Go runtime may create and terminate threads arbitrarily, the amount of memory leaked has the potential to grow unbounded. Clean up the OpenSSL error queue for a thread upon thread exit. Use the native threading APIs to arrange a callback on thread exit as the Go runtime does not provide any similar facilities itself. No explicit thread-exit handling is needed for OpenSSL 1.1.0 and above as it automatically deallocates thread-local resources without application assistance. See the OPENSSL_thread_stop(3) man page for more information. Signed-off-by: Cory Snider <[email protected]>
- Loading branch information
Showing
7 changed files
with
122 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package openssl | ||
|
||
// Go wrappers for testing the thread setup code as _test.go files cannot import "C". | ||
|
||
// #include "thread_setup.h" | ||
import "C" | ||
|
||
// opensslThreadsCleanedUp returns the number of times the thread-local OpenSSL | ||
// state has been cleaned up since the process started. | ||
func opensslThreadsCleanedUp() uint { | ||
return uint(C.go_openssl_threads_cleaned_up) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#define CRYPTO_LOCK 0x01 | ||
|
||
/* Used by unit tests. */ | ||
extern volatile unsigned int go_openssl_threads_cleaned_up; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package openssl | ||
|
||
import ( | ||
"runtime" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func init() { | ||
// The runtime parks the "main" thread of the process on Linux rather of | ||
// terminating it. Lock the main thread to the initial goroutine to ensure | ||
// that the test goroutines will always be scheduled onto non-main threads | ||
// that can be consistently made to terminate on demand. | ||
runtime.LockOSThread() | ||
} | ||
|
||
func TestThreadCleanup(t *testing.T) { | ||
if vMajor > 1 || vMinor > 0 { | ||
t.Skip("explicit thread cleanup is only needed for OpenSSL 1.0.x") | ||
} | ||
|
||
before := opensslThreadsCleanedUp() | ||
done := make(chan struct{}) | ||
go func() { | ||
defer close(done) | ||
// The thread this goroutine is running on will be terminated by the | ||
// runtime when the goroutine exits. | ||
runtime.LockOSThread() | ||
// Checking for errors has the side effect of initializing | ||
// the thread-local OpenSSL error queue. | ||
_ = newOpenSSLError("") | ||
}() | ||
<-done | ||
time.Sleep(100 * time.Millisecond) // Give some time for the thread to terminate. | ||
after := opensslThreadsCleanedUp() | ||
|
||
if n := after - before; n != 1 { | ||
t.Errorf("expected thread cleanup to have run once, but it ran %d times", n) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters