From db82cf4e506938a36a57a64dbe1f79eb0365ea89 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Mon, 26 Sep 2016 16:44:35 +1000 Subject: [PATCH] runtime: use RtlGenRandom instead of CryptGenRandom This change replaces the use of CryptGenRandom with RtlGenRandom in Windows to generate cryptographically random numbers during process startup. RtlGenRandom uses the same RNG as CryptGenRandom, but it has many fewer DLL dependencies and so does not affect process startup time as much. This makes running simple Go program on my computers faster. Windows XP: benchmark old ns/op new ns/op delta BenchmarkRunningGoProgram-2 47408573 10784148 -77.25% Windows 7 (VM): benchmark old ns/op new ns/op delta BenchmarkRunningGoProgram 16260390 12792150 -21.33% Windows 7: benchmark old ns/op new ns/op delta BenchmarkRunningGoProgram-2 13600778 10050574 -26.10% Fixes #15589 Change-Id: I2816239a2056e3d4a6dcd86a6fa2bb619c6008fe Reviewed-on: https://go-review.googlesource.com/29700 Reviewed-by: Brad Fitzpatrick --- src/runtime/os_windows.go | 35 ++++++++++++++------------ src/runtime/syscall_windows_test.go | 38 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 95088ac751..0f52d7d470 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -20,9 +20,6 @@ const ( //go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll" //go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll" //go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll" -//go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW%5 "advapi32.dll" -//go:cgo_import_dynamic runtime._CryptGenRandom CryptGenRandom%3 "advapi32.dll" -//go:cgo_import_dynamic runtime._CryptReleaseContext CryptReleaseContext%2 "advapi32.dll" //go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll" //go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll" //go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll" @@ -67,9 +64,6 @@ var ( _CreateIoCompletionPort, _CreateThread, _CreateWaitableTimerA, - _CryptAcquireContextW, - _CryptGenRandom, - _CryptReleaseContext, _DuplicateHandle, _ExitProcess, _FreeEnvironmentStringsW, @@ -110,6 +104,16 @@ var ( _GetQueuedCompletionStatusEx, _LoadLibraryExW, _ stdFunction + + // Use RtlGenRandom to generate cryptographically random data. + // This approach has been recommended by Microsoft (see issue + // 15589 for details). + // The RtlGenRandom is not listed in advapi32.dll, instead + // RtlGenRandom function can be found by searching for SystemFunction036. + // Also some versions of Mingw cannot link to SystemFunction036 + // when building executable as Cgo. So load SystemFunction036 + // manually during runtime startup. + _RtlGenRandom stdFunction ) // Function to be called by windows CreateThread @@ -167,6 +171,13 @@ func loadOptionalSyscalls() { _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) _GetQueuedCompletionStatusEx = windowsFindfunc(k32, []byte("GetQueuedCompletionStatusEx\000")) _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) + + var advapi32dll = []byte("advapi32.dll\000") + a32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&advapi32dll[0]))) + if a32 == 0 { + throw("advapi32.dll not found") + } + _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) } //go:nosplit @@ -273,17 +284,9 @@ func osinit() { //go:nosplit func getRandomData(r []byte) { - const ( - prov_rsa_full = 1 - crypt_verifycontext = 0xF0000000 - ) - var handle uintptr n := 0 - if stdcall5(_CryptAcquireContextW, uintptr(unsafe.Pointer(&handle)), 0, 0, prov_rsa_full, crypt_verifycontext) != 0 { - if stdcall3(_CryptGenRandom, handle, uintptr(len(r)), uintptr(unsafe.Pointer(&r[0]))) != 0 { - n = len(r) - } - stdcall2(_CryptReleaseContext, handle, 0) + if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { + n = len(r) } extendRandom(r, n) } diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go index 4a10749682..c19cd71662 100644 --- a/src/runtime/syscall_windows_test.go +++ b/src/runtime/syscall_windows_test.go @@ -972,3 +972,41 @@ func BenchmarkOsYield(b *testing.B) { runtime.OsYield() } } + +func BenchmarkRunningGoProgram(b *testing.B) { + tmpdir, err := ioutil.TempDir("", "BenchmarkRunningGoProgram") + if err != nil { + b.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + src := filepath.Join(tmpdir, "main.go") + err = ioutil.WriteFile(src, []byte(benchmarkRunnigGoProgram), 0666) + if err != nil { + b.Fatal(err) + } + + exe := filepath.Join(tmpdir, "main.exe") + cmd := exec.Command("go", "build", "-o", exe, src) + cmd.Dir = tmpdir + out, err := cmd.CombinedOutput() + if err != nil { + b.Fatalf("building main.exe failed: %v\n%s", err, out) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + cmd := exec.Command(exe) + out, err := cmd.CombinedOutput() + if err != nil { + b.Fatalf("runing main.exe failed: %v\n%s", err, out) + } + } +} + +const benchmarkRunnigGoProgram = ` +package main + +func main() { +} +`