github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/runtime/syscall_windows_test.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "internal/syscall/windows/sysdll" 11 "internal/testenv" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "runtime" 17 "strings" 18 "syscall" 19 "testing" 20 "unsafe" 21 ) 22 23 type DLL struct { 24 *syscall.DLL 25 t *testing.T 26 } 27 28 func GetDLL(t *testing.T, name string) *DLL { 29 d, e := syscall.LoadDLL(name) 30 if e != nil { 31 t.Fatal(e) 32 } 33 return &DLL{DLL: d, t: t} 34 } 35 36 func (d *DLL) Proc(name string) *syscall.Proc { 37 p, e := d.FindProc(name) 38 if e != nil { 39 d.t.Fatal(e) 40 } 41 return p 42 } 43 44 func TestStdCall(t *testing.T) { 45 type Rect struct { 46 left, top, right, bottom int32 47 } 48 res := Rect{} 49 expected := Rect{1, 1, 40, 60} 50 a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call( 51 uintptr(unsafe.Pointer(&res)), 52 uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})), 53 uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50}))) 54 if a != 1 || res.left != expected.left || 55 res.top != expected.top || 56 res.right != expected.right || 57 res.bottom != expected.bottom { 58 t.Error("stdcall USER32.UnionRect returns", a, "res=", res) 59 } 60 } 61 62 func Test64BitReturnStdCall(t *testing.T) { 63 64 const ( 65 VER_BUILDNUMBER = 0x0000004 66 VER_MAJORVERSION = 0x0000002 67 VER_MINORVERSION = 0x0000001 68 VER_PLATFORMID = 0x0000008 69 VER_PRODUCT_TYPE = 0x0000080 70 VER_SERVICEPACKMAJOR = 0x0000020 71 VER_SERVICEPACKMINOR = 0x0000010 72 VER_SUITENAME = 0x0000040 73 74 VER_EQUAL = 1 75 VER_GREATER = 2 76 VER_GREATER_EQUAL = 3 77 VER_LESS = 4 78 VER_LESS_EQUAL = 5 79 80 ERROR_OLD_WIN_VERSION syscall.Errno = 1150 81 ) 82 83 type OSVersionInfoEx struct { 84 OSVersionInfoSize uint32 85 MajorVersion uint32 86 MinorVersion uint32 87 BuildNumber uint32 88 PlatformId uint32 89 CSDVersion [128]uint16 90 ServicePackMajor uint16 91 ServicePackMinor uint16 92 SuiteMask uint16 93 ProductType byte 94 Reserve byte 95 } 96 97 d := GetDLL(t, "kernel32.dll") 98 99 var m1, m2 uintptr 100 VerSetConditionMask := d.Proc("VerSetConditionMask") 101 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL) 102 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL) 103 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL) 104 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL) 105 106 vi := OSVersionInfoEx{ 107 MajorVersion: 5, 108 MinorVersion: 1, 109 ServicePackMajor: 2, 110 ServicePackMinor: 0, 111 } 112 vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi)) 113 r, _, e2 := d.Proc("VerifyVersionInfoW").Call( 114 uintptr(unsafe.Pointer(&vi)), 115 VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR, 116 m1, m2) 117 if r == 0 && e2 != ERROR_OLD_WIN_VERSION { 118 t.Errorf("VerifyVersionInfo failed: %s", e2) 119 } 120 } 121 122 func TestCDecl(t *testing.T) { 123 var buf [50]byte 124 fmtp, _ := syscall.BytePtrFromString("%d %d %d") 125 a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call( 126 uintptr(unsafe.Pointer(&buf[0])), 127 uintptr(unsafe.Pointer(fmtp)), 128 1000, 2000, 3000) 129 if string(buf[:a]) != "1000 2000 3000" { 130 t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a]) 131 } 132 } 133 134 func TestEnumWindows(t *testing.T) { 135 d := GetDLL(t, "user32.dll") 136 isWindows := d.Proc("IsWindow") 137 counter := 0 138 cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr { 139 if lparam != 888 { 140 t.Error("lparam was not passed to callback") 141 } 142 b, _, _ := isWindows.Call(uintptr(hwnd)) 143 if b == 0 { 144 t.Error("USER32.IsWindow returns FALSE") 145 } 146 counter++ 147 return 1 // continue enumeration 148 }) 149 a, _, _ := d.Proc("EnumWindows").Call(cb, 888) 150 if a == 0 { 151 t.Error("USER32.EnumWindows returns FALSE") 152 } 153 if counter == 0 { 154 t.Error("Callback has been never called or your have no windows") 155 } 156 } 157 158 func callback(hwnd syscall.Handle, lparam uintptr) uintptr { 159 (*(*func())(unsafe.Pointer(&lparam)))() 160 return 0 // stop enumeration 161 } 162 163 // nestedCall calls into Windows, back into Go, and finally to f. 164 func nestedCall(t *testing.T, f func()) { 165 c := syscall.NewCallback(callback) 166 d := GetDLL(t, "user32.dll") 167 defer d.Release() 168 d.Proc("EnumWindows").Call(c, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f)))) 169 } 170 171 func TestCallback(t *testing.T) { 172 var x = false 173 nestedCall(t, func() { x = true }) 174 if !x { 175 t.Fatal("nestedCall did not call func") 176 } 177 } 178 179 func TestCallbackGC(t *testing.T) { 180 nestedCall(t, runtime.GC) 181 } 182 183 func TestCallbackPanicLocked(t *testing.T) { 184 runtime.LockOSThread() 185 defer runtime.UnlockOSThread() 186 187 if !runtime.LockedOSThread() { 188 t.Fatal("runtime.LockOSThread didn't") 189 } 190 defer func() { 191 s := recover() 192 if s == nil { 193 t.Fatal("did not panic") 194 } 195 if s.(string) != "callback panic" { 196 t.Fatal("wrong panic:", s) 197 } 198 if !runtime.LockedOSThread() { 199 t.Fatal("lost lock on OS thread after panic") 200 } 201 }() 202 nestedCall(t, func() { panic("callback panic") }) 203 panic("nestedCall returned") 204 } 205 206 func TestCallbackPanic(t *testing.T) { 207 // Make sure panic during callback unwinds properly. 208 if runtime.LockedOSThread() { 209 t.Fatal("locked OS thread on entry to TestCallbackPanic") 210 } 211 defer func() { 212 s := recover() 213 if s == nil { 214 t.Fatal("did not panic") 215 } 216 if s.(string) != "callback panic" { 217 t.Fatal("wrong panic:", s) 218 } 219 if runtime.LockedOSThread() { 220 t.Fatal("locked OS thread on exit from TestCallbackPanic") 221 } 222 }() 223 nestedCall(t, func() { panic("callback panic") }) 224 panic("nestedCall returned") 225 } 226 227 func TestCallbackPanicLoop(t *testing.T) { 228 // Make sure we don't blow out m->g0 stack. 229 for i := 0; i < 100000; i++ { 230 TestCallbackPanic(t) 231 } 232 } 233 234 func TestBlockingCallback(t *testing.T) { 235 c := make(chan int) 236 go func() { 237 for i := 0; i < 10; i++ { 238 c <- <-c 239 } 240 }() 241 nestedCall(t, func() { 242 for i := 0; i < 10; i++ { 243 c <- i 244 if j := <-c; j != i { 245 t.Errorf("out of sync %d != %d", j, i) 246 } 247 } 248 }) 249 } 250 251 func TestCallbackInAnotherThread(t *testing.T) { 252 // TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread() 253 } 254 255 type cbDLLFunc int // int determines number of callback parameters 256 257 func (f cbDLLFunc) stdcallName() string { 258 return fmt.Sprintf("stdcall%d", f) 259 } 260 261 func (f cbDLLFunc) cdeclName() string { 262 return fmt.Sprintf("cdecl%d", f) 263 } 264 265 func (f cbDLLFunc) buildOne(stdcall bool) string { 266 var funcname, attr string 267 if stdcall { 268 funcname = f.stdcallName() 269 attr = "__stdcall" 270 } else { 271 funcname = f.cdeclName() 272 attr = "__cdecl" 273 } 274 typename := "t" + funcname 275 p := make([]string, f) 276 for i := range p { 277 p[i] = "uintptr_t" 278 } 279 params := strings.Join(p, ",") 280 for i := range p { 281 p[i] = fmt.Sprintf("%d", i+1) 282 } 283 args := strings.Join(p, ",") 284 return fmt.Sprintf(` 285 typedef void %s (*%s)(%s); 286 void %s(%s f, uintptr_t n) { 287 uintptr_t i; 288 for(i=0;i<n;i++){ 289 f(%s); 290 } 291 } 292 `, attr, typename, params, funcname, typename, args) 293 } 294 295 func (f cbDLLFunc) build() string { 296 return "#include <stdint.h>\n\n" + f.buildOne(false) + f.buildOne(true) 297 } 298 299 var cbFuncs = [...]interface{}{ 300 2: func(i1, i2 uintptr) uintptr { 301 if i1+i2 != 3 { 302 panic("bad input") 303 } 304 return 0 305 }, 306 3: func(i1, i2, i3 uintptr) uintptr { 307 if i1+i2+i3 != 6 { 308 panic("bad input") 309 } 310 return 0 311 }, 312 4: func(i1, i2, i3, i4 uintptr) uintptr { 313 if i1+i2+i3+i4 != 10 { 314 panic("bad input") 315 } 316 return 0 317 }, 318 5: func(i1, i2, i3, i4, i5 uintptr) uintptr { 319 if i1+i2+i3+i4+i5 != 15 { 320 panic("bad input") 321 } 322 return 0 323 }, 324 6: func(i1, i2, i3, i4, i5, i6 uintptr) uintptr { 325 if i1+i2+i3+i4+i5+i6 != 21 { 326 panic("bad input") 327 } 328 return 0 329 }, 330 7: func(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr { 331 if i1+i2+i3+i4+i5+i6+i7 != 28 { 332 panic("bad input") 333 } 334 return 0 335 }, 336 8: func(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr { 337 if i1+i2+i3+i4+i5+i6+i7+i8 != 36 { 338 panic("bad input") 339 } 340 return 0 341 }, 342 9: func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr { 343 if i1+i2+i3+i4+i5+i6+i7+i8+i9 != 45 { 344 panic("bad input") 345 } 346 return 0 347 }, 348 } 349 350 type cbDLL struct { 351 name string 352 buildArgs func(out, src string) []string 353 } 354 355 func (d *cbDLL) buildSrc(t *testing.T, path string) { 356 f, err := os.Create(path) 357 if err != nil { 358 t.Fatalf("failed to create source file: %v", err) 359 } 360 defer f.Close() 361 362 for i := 2; i < 10; i++ { 363 fmt.Fprint(f, cbDLLFunc(i).build()) 364 } 365 } 366 367 func (d *cbDLL) build(t *testing.T, dir string) string { 368 srcname := d.name + ".c" 369 d.buildSrc(t, filepath.Join(dir, srcname)) 370 outname := d.name + ".dll" 371 args := d.buildArgs(outname, srcname) 372 cmd := exec.Command(args[0], args[1:]...) 373 cmd.Dir = dir 374 out, err := cmd.CombinedOutput() 375 if err != nil { 376 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 377 } 378 return filepath.Join(dir, outname) 379 } 380 381 var cbDLLs = []cbDLL{ 382 { 383 "test", 384 func(out, src string) []string { 385 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src} 386 }, 387 }, 388 { 389 "testO2", 390 func(out, src string) []string { 391 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src} 392 }, 393 }, 394 } 395 396 type cbTest struct { 397 n int // number of callback parameters 398 param uintptr // dll function parameter 399 } 400 401 func (test *cbTest) run(t *testing.T, dllpath string) { 402 dll := syscall.MustLoadDLL(dllpath) 403 defer dll.Release() 404 cb := cbFuncs[test.n] 405 stdcall := syscall.NewCallback(cb) 406 f := cbDLLFunc(test.n) 407 test.runOne(t, dll, f.stdcallName(), stdcall) 408 cdecl := syscall.NewCallbackCDecl(cb) 409 test.runOne(t, dll, f.cdeclName(), cdecl) 410 } 411 412 func (test *cbTest) runOne(t *testing.T, dll *syscall.DLL, proc string, cb uintptr) { 413 defer func() { 414 if r := recover(); r != nil { 415 t.Errorf("dll call %v(..., %d) failed: %v", proc, test.param, r) 416 } 417 }() 418 dll.MustFindProc(proc).Call(cb, test.param) 419 } 420 421 var cbTests = []cbTest{ 422 {2, 1}, 423 {2, 10000}, 424 {3, 3}, 425 {4, 5}, 426 {4, 6}, 427 {5, 2}, 428 {6, 7}, 429 {6, 8}, 430 {7, 6}, 431 {8, 1}, 432 {9, 8}, 433 {9, 10000}, 434 {3, 4}, 435 {5, 3}, 436 {7, 7}, 437 {8, 2}, 438 {9, 9}, 439 } 440 441 func TestStdcallAndCDeclCallbacks(t *testing.T) { 442 if _, err := exec.LookPath("gcc"); err != nil { 443 t.Skip("skipping test: gcc is missing") 444 } 445 tmp, err := ioutil.TempDir("", "TestCDeclCallback") 446 if err != nil { 447 t.Fatal("TempDir failed: ", err) 448 } 449 defer os.RemoveAll(tmp) 450 451 for _, dll := range cbDLLs { 452 dllPath := dll.build(t, tmp) 453 for _, test := range cbTests { 454 test.run(t, dllPath) 455 } 456 } 457 } 458 459 func TestRegisterClass(t *testing.T) { 460 kernel32 := GetDLL(t, "kernel32.dll") 461 user32 := GetDLL(t, "user32.dll") 462 mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0) 463 cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) { 464 t.Fatal("callback should never get called") 465 return 0 466 }) 467 type Wndclassex struct { 468 Size uint32 469 Style uint32 470 WndProc uintptr 471 ClsExtra int32 472 WndExtra int32 473 Instance syscall.Handle 474 Icon syscall.Handle 475 Cursor syscall.Handle 476 Background syscall.Handle 477 MenuName *uint16 478 ClassName *uint16 479 IconSm syscall.Handle 480 } 481 name := syscall.StringToUTF16Ptr("test_window") 482 wc := Wndclassex{ 483 WndProc: cb, 484 Instance: syscall.Handle(mh), 485 ClassName: name, 486 } 487 wc.Size = uint32(unsafe.Sizeof(wc)) 488 a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc))) 489 if a == 0 { 490 t.Fatalf("RegisterClassEx failed: %v", err) 491 } 492 r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0) 493 if r == 0 { 494 t.Fatalf("UnregisterClass failed: %v", err) 495 } 496 } 497 498 func TestOutputDebugString(t *testing.T) { 499 d := GetDLL(t, "kernel32.dll") 500 p := syscall.StringToUTF16Ptr("testing OutputDebugString") 501 d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p))) 502 } 503 504 func TestRaiseException(t *testing.T) { 505 o := runTestProg(t, "testprog", "RaiseException") 506 if strings.Contains(o, "RaiseException should not return") { 507 t.Fatalf("RaiseException did not crash program: %v", o) 508 } 509 if !strings.Contains(o, "Exception 0xbad") { 510 t.Fatalf("No stack trace: %v", o) 511 } 512 } 513 514 func TestZeroDivisionException(t *testing.T) { 515 o := runTestProg(t, "testprog", "ZeroDivisionException") 516 if !strings.Contains(o, "panic: runtime error: integer divide by zero") { 517 t.Fatalf("No stack trace: %v", o) 518 } 519 } 520 521 func TestWERDialogue(t *testing.T) { 522 if os.Getenv("TESTING_WER_DIALOGUE") == "1" { 523 defer os.Exit(0) 524 525 *runtime.TestingWER = true 526 const EXCEPTION_NONCONTINUABLE = 1 527 mod := syscall.MustLoadDLL("kernel32.dll") 528 proc := mod.MustFindProc("RaiseException") 529 proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0) 530 println("RaiseException should not return") 531 return 532 } 533 cmd := exec.Command(os.Args[0], "-test.run=TestWERDialogue") 534 cmd.Env = []string{"TESTING_WER_DIALOGUE=1"} 535 // Child process should not open WER dialogue, but return immediately instead. 536 cmd.CombinedOutput() 537 } 538 539 var used byte 540 541 func use(buf []byte) { 542 for _, c := range buf { 543 used += c 544 } 545 } 546 547 func forceStackCopy() (r int) { 548 var f func(int) int 549 f = func(i int) int { 550 var buf [256]byte 551 use(buf[:]) 552 if i == 0 { 553 return 0 554 } 555 return i + f(i-1) 556 } 557 r = f(128) 558 return 559 } 560 561 func TestReturnAfterStackGrowInCallback(t *testing.T) { 562 if _, err := exec.LookPath("gcc"); err != nil { 563 t.Skip("skipping test: gcc is missing") 564 } 565 566 const src = ` 567 #include <stdint.h> 568 #include <windows.h> 569 570 typedef uintptr_t __stdcall (*callback)(uintptr_t); 571 572 uintptr_t cfunc(callback f, uintptr_t n) { 573 uintptr_t r; 574 r = f(n); 575 SetLastError(333); 576 return r; 577 } 578 ` 579 tmpdir, err := ioutil.TempDir("", "TestReturnAfterStackGrowInCallback") 580 if err != nil { 581 t.Fatal("TempDir failed: ", err) 582 } 583 defer os.RemoveAll(tmpdir) 584 585 srcname := "mydll.c" 586 err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 587 if err != nil { 588 t.Fatal(err) 589 } 590 outname := "mydll.dll" 591 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 592 cmd.Dir = tmpdir 593 out, err := cmd.CombinedOutput() 594 if err != nil { 595 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 596 } 597 dllpath := filepath.Join(tmpdir, outname) 598 599 dll := syscall.MustLoadDLL(dllpath) 600 defer dll.Release() 601 602 proc := dll.MustFindProc("cfunc") 603 604 cb := syscall.NewCallback(func(n uintptr) uintptr { 605 forceStackCopy() 606 return n 607 }) 608 609 // Use a new goroutine so that we get a small stack. 610 type result struct { 611 r uintptr 612 err syscall.Errno 613 } 614 c := make(chan result) 615 go func() { 616 r, _, err := proc.Call(cb, 100) 617 c <- result{r, err.(syscall.Errno)} 618 }() 619 want := result{r: 100, err: 333} 620 if got := <-c; got != want { 621 t.Errorf("got %d want %d", got, want) 622 } 623 } 624 625 func TestTimeBeginPeriod(t *testing.T) { 626 const TIMERR_NOERROR = 0 627 if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR { 628 t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue) 629 } 630 } 631 632 // removeOneCPU removes one (any) cpu from affinity mask. 633 // It returns new affinity mask. 634 func removeOneCPU(mask uintptr) (uintptr, error) { 635 if mask == 0 { 636 return 0, fmt.Errorf("cpu affinity mask is empty") 637 } 638 maskbits := int(unsafe.Sizeof(mask) * 8) 639 for i := 0; i < maskbits; i++ { 640 newmask := mask & ^(1 << uint(i)) 641 if newmask != mask { 642 return newmask, nil 643 } 644 645 } 646 panic("not reached") 647 } 648 649 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error { 650 _OpenThread := kernel32.MustFindProc("OpenThread") 651 _ResumeThread := kernel32.MustFindProc("ResumeThread") 652 _Thread32First := kernel32.MustFindProc("Thread32First") 653 _Thread32Next := kernel32.MustFindProc("Thread32Next") 654 655 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0) 656 if err != nil { 657 return err 658 } 659 defer syscall.CloseHandle(snapshot) 660 661 const _THREAD_SUSPEND_RESUME = 0x0002 662 663 type ThreadEntry32 struct { 664 Size uint32 665 tUsage uint32 666 ThreadID uint32 667 OwnerProcessID uint32 668 BasePri int32 669 DeltaPri int32 670 Flags uint32 671 } 672 673 var te ThreadEntry32 674 te.Size = uint32(unsafe.Sizeof(te)) 675 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 676 if ret == 0 { 677 return err 678 } 679 for te.OwnerProcessID != uint32(childpid) { 680 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 681 if ret == 0 { 682 return err 683 } 684 } 685 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID)) 686 if h == 0 { 687 return err 688 } 689 defer syscall.Close(syscall.Handle(h)) 690 691 ret, _, err = _ResumeThread.Call(h) 692 if ret == 0xffffffff { 693 return err 694 } 695 return nil 696 } 697 698 func TestNumCPU(t *testing.T) { 699 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 700 // in child process 701 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU()) 702 os.Exit(0) 703 } 704 705 switch n := runtime.NumberOfProcessors(); { 706 case n < 1: 707 t.Fatalf("system cannot have %d cpu(s)", n) 708 case n == 1: 709 if runtime.NumCPU() != 1 { 710 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU()) 711 } 712 return 713 } 714 715 const ( 716 _CREATE_SUSPENDED = 0x00000004 717 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff 718 ) 719 720 kernel32 := syscall.MustLoadDLL("kernel32.dll") 721 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask") 722 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask") 723 724 cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU") 725 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") 726 var buf bytes.Buffer 727 cmd.Stdout = &buf 728 cmd.Stderr = &buf 729 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED} 730 err := cmd.Start() 731 if err != nil { 732 t.Fatal(err) 733 } 734 defer func() { 735 err = cmd.Wait() 736 childOutput := string(buf.Bytes()) 737 if err != nil { 738 t.Fatalf("child failed: %v: %v", err, childOutput) 739 } 740 // removeOneCPU should have decreased child cpu count by 1 741 want := fmt.Sprintf("%d", runtime.NumCPU()-1) 742 if childOutput != want { 743 t.Fatalf("child output: want %q, got %q", want, childOutput) 744 } 745 }() 746 747 defer func() { 748 err = resumeChildThread(kernel32, cmd.Process.Pid) 749 if err != nil { 750 t.Fatal(err) 751 } 752 }() 753 754 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid)) 755 if err != nil { 756 t.Fatal(err) 757 } 758 defer syscall.CloseHandle(ph) 759 760 var mask, sysmask uintptr 761 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 762 if ret == 0 { 763 t.Fatal(err) 764 } 765 766 newmask, err := removeOneCPU(mask) 767 if err != nil { 768 t.Fatal(err) 769 } 770 771 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask) 772 if ret == 0 { 773 t.Fatal(err) 774 } 775 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 776 if ret == 0 { 777 t.Fatal(err) 778 } 779 if newmask != mask { 780 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask) 781 } 782 } 783 784 // See Issue 14959 785 func TestDLLPreloadMitigation(t *testing.T) { 786 if _, err := exec.LookPath("gcc"); err != nil { 787 t.Skip("skipping test: gcc is missing") 788 } 789 790 tmpdir, err := ioutil.TempDir("", "TestDLLPreloadMitigation") 791 if err != nil { 792 t.Fatal("TempDir failed: ", err) 793 } 794 defer func() { 795 err := os.RemoveAll(tmpdir) 796 if err != nil { 797 t.Error(err) 798 } 799 }() 800 801 dir0, err := os.Getwd() 802 if err != nil { 803 t.Fatal(err) 804 } 805 defer os.Chdir(dir0) 806 807 const src = ` 808 #include <stdint.h> 809 #include <windows.h> 810 811 uintptr_t cfunc() { 812 SetLastError(123); 813 } 814 ` 815 srcname := "nojack.c" 816 err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 817 if err != nil { 818 t.Fatal(err) 819 } 820 name := "nojack.dll" 821 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname) 822 cmd.Dir = tmpdir 823 out, err := cmd.CombinedOutput() 824 if err != nil { 825 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 826 } 827 dllpath := filepath.Join(tmpdir, name) 828 829 dll := syscall.MustLoadDLL(dllpath) 830 dll.MustFindProc("cfunc") 831 dll.Release() 832 833 // Get into the directory with the DLL we'll load by base name 834 // ("nojack.dll") Think of this as the user double-clicking an 835 // installer from their Downloads directory where a browser 836 // silently downloaded some malicious DLLs. 837 os.Chdir(tmpdir) 838 839 // First before we can load a DLL from the current directory, 840 // loading it only as "nojack.dll", without an absolute path. 841 delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly 842 dll, err = syscall.LoadDLL(name) 843 if err != nil { 844 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err) 845 } 846 dll.Release() 847 848 // And now verify that if we register it as a system32-only 849 // DLL, the implicit loading from the current directory no 850 // longer works. 851 sysdll.IsSystemDLL[name] = true 852 dll, err = syscall.LoadDLL(name) 853 if err == nil { 854 dll.Release() 855 if wantLoadLibraryEx() { 856 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err) 857 } 858 t.Skip("insecure load of DLL, but expected") 859 } 860 } 861 862 // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests. 863 func wantLoadLibraryEx() bool { 864 return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce" 865 } 866 867 func TestLoadLibraryEx(t *testing.T) { 868 use, have, flags := runtime.LoadLibraryExStatus() 869 if use { 870 return // success. 871 } 872 if wantLoadLibraryEx() { 873 t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)", 874 have, flags) 875 } 876 t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)", 877 have, flags) 878 } 879 880 var ( 881 modwinmm = syscall.NewLazyDLL("winmm.dll") 882 modkernel32 = syscall.NewLazyDLL("kernel32.dll") 883 884 procCreateEvent = modkernel32.NewProc("CreateEventW") 885 procSetEvent = modkernel32.NewProc("SetEvent") 886 ) 887 888 func createEvent() (syscall.Handle, error) { 889 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0) 890 if r0 == 0 { 891 return 0, syscall.Errno(e0) 892 } 893 return syscall.Handle(r0), nil 894 } 895 896 func setEvent(h syscall.Handle) error { 897 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0) 898 if r0 == 0 { 899 return syscall.Errno(e0) 900 } 901 return nil 902 } 903 904 func BenchmarkChanToSyscallPing(b *testing.B) { 905 n := b.N 906 ch := make(chan int) 907 event, err := createEvent() 908 if err != nil { 909 b.Fatal(err) 910 } 911 go func() { 912 for i := 0; i < n; i++ { 913 syscall.WaitForSingleObject(event, syscall.INFINITE) 914 ch <- 1 915 } 916 }() 917 for i := 0; i < n; i++ { 918 err := setEvent(event) 919 if err != nil { 920 b.Fatal(err) 921 } 922 <-ch 923 } 924 } 925 926 func BenchmarkSyscallToSyscallPing(b *testing.B) { 927 n := b.N 928 event1, err := createEvent() 929 if err != nil { 930 b.Fatal(err) 931 } 932 event2, err := createEvent() 933 if err != nil { 934 b.Fatal(err) 935 } 936 go func() { 937 for i := 0; i < n; i++ { 938 syscall.WaitForSingleObject(event1, syscall.INFINITE) 939 err := setEvent(event2) 940 if err != nil { 941 b.Fatal(err) 942 } 943 } 944 }() 945 for i := 0; i < n; i++ { 946 err := setEvent(event1) 947 if err != nil { 948 b.Fatal(err) 949 } 950 syscall.WaitForSingleObject(event2, syscall.INFINITE) 951 } 952 } 953 954 func BenchmarkChanToChanPing(b *testing.B) { 955 n := b.N 956 ch1 := make(chan int) 957 ch2 := make(chan int) 958 go func() { 959 for i := 0; i < n; i++ { 960 <-ch1 961 ch2 <- 1 962 } 963 }() 964 for i := 0; i < n; i++ { 965 ch1 <- 1 966 <-ch2 967 } 968 } 969 970 func BenchmarkOsYield(b *testing.B) { 971 for i := 0; i < b.N; i++ { 972 runtime.OsYield() 973 } 974 }