]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use RtlGenRandom instead of CryptGenRandom
authorAlex Brainman <alex.brainman@gmail.com>
Mon, 26 Sep 2016 06:44:35 +0000 (16:44 +1000)
committerAlex Brainman <alex.brainman@gmail.com>
Wed, 28 Sep 2016 05:21:53 +0000 (05:21 +0000)
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 <bradfitz@golang.org>
src/runtime/os_windows.go
src/runtime/syscall_windows_test.go

index 95088ac751937e2e81222fa8aaf1c3927f064922..0f52d7d4703ff145b44f16fe2cffedf86726ed41 100644 (file)
@@ -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)
 }
index 4a10749682414f9c4d36fb872c3014ca9fa474ba..c19cd71662ff4bec5fedefab3dd5aff56531720d 100644 (file)
@@ -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() {
+}
+`