github.com/aloncn/graphics-go@v0.0.1/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 // removeOneCPU removes one (any) cpu from affinity mask. 626 // It returns new affinity mask. 627 func removeOneCPU(mask uintptr) (uintptr, error) { 628 if mask == 0 { 629 return 0, fmt.Errorf("cpu affinity mask is empty") 630 } 631 maskbits := int(unsafe.Sizeof(mask) * 8) 632 for i := 0; i < maskbits; i++ { 633 newmask := mask & ^(1 << uint(i)) 634 if newmask != mask { 635 return newmask, nil 636 } 637 638 } 639 panic("not reached") 640 } 641 642 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error { 643 _OpenThread := kernel32.MustFindProc("OpenThread") 644 _ResumeThread := kernel32.MustFindProc("ResumeThread") 645 _Thread32First := kernel32.MustFindProc("Thread32First") 646 _Thread32Next := kernel32.MustFindProc("Thread32Next") 647 648 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0) 649 if err != nil { 650 return err 651 } 652 defer syscall.CloseHandle(snapshot) 653 654 const _THREAD_SUSPEND_RESUME = 0x0002 655 656 type ThreadEntry32 struct { 657 Size uint32 658 tUsage uint32 659 ThreadID uint32 660 OwnerProcessID uint32 661 BasePri int32 662 DeltaPri int32 663 Flags uint32 664 } 665 666 var te ThreadEntry32 667 te.Size = uint32(unsafe.Sizeof(te)) 668 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 669 if ret == 0 { 670 return err 671 } 672 for te.OwnerProcessID != uint32(childpid) { 673 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 674 if ret == 0 { 675 return err 676 } 677 } 678 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID)) 679 if h == 0 { 680 return err 681 } 682 defer syscall.Close(syscall.Handle(h)) 683 684 ret, _, err = _ResumeThread.Call(h) 685 if ret == 0xffffffff { 686 return err 687 } 688 return nil 689 } 690 691 func TestNumCPU(t *testing.T) { 692 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 693 // in child process 694 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU()) 695 os.Exit(0) 696 } 697 698 switch n := runtime.NumberOfProcessors(); { 699 case n < 1: 700 t.Fatalf("system cannot have %d cpu(s)", n) 701 case n == 1: 702 if runtime.NumCPU() != 1 { 703 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU()) 704 } 705 return 706 } 707 708 const ( 709 _CREATE_SUSPENDED = 0x00000004 710 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff 711 ) 712 713 kernel32 := syscall.MustLoadDLL("kernel32.dll") 714 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask") 715 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask") 716 717 cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU") 718 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") 719 var buf bytes.Buffer 720 cmd.Stdout = &buf 721 cmd.Stderr = &buf 722 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED} 723 err := cmd.Start() 724 if err != nil { 725 t.Fatal(err) 726 } 727 defer func() { 728 err = cmd.Wait() 729 childOutput := string(buf.Bytes()) 730 if err != nil { 731 t.Fatalf("child failed: %v: %v", err, childOutput) 732 } 733 // removeOneCPU should have decreased child cpu count by 1 734 want := fmt.Sprintf("%d", runtime.NumCPU()-1) 735 if childOutput != want { 736 t.Fatalf("child output: want %q, got %q", want, childOutput) 737 } 738 }() 739 740 defer func() { 741 err = resumeChildThread(kernel32, cmd.Process.Pid) 742 if err != nil { 743 t.Fatal(err) 744 } 745 }() 746 747 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid)) 748 if err != nil { 749 t.Fatal(err) 750 } 751 defer syscall.CloseHandle(ph) 752 753 var mask, sysmask uintptr 754 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 755 if ret == 0 { 756 t.Fatal(err) 757 } 758 759 newmask, err := removeOneCPU(mask) 760 if err != nil { 761 t.Fatal(err) 762 } 763 764 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask) 765 if ret == 0 { 766 t.Fatal(err) 767 } 768 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 769 if ret == 0 { 770 t.Fatal(err) 771 } 772 if newmask != mask { 773 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask) 774 } 775 } 776 777 // See Issue 14959 778 func TestDLLPreloadMitigation(t *testing.T) { 779 if _, err := exec.LookPath("gcc"); err != nil { 780 t.Skip("skipping test: gcc is missing") 781 } 782 783 tmpdir, err := ioutil.TempDir("", "TestDLLPreloadMitigation") 784 if err != nil { 785 t.Fatal("TempDir failed: ", err) 786 } 787 defer func() { 788 err := os.RemoveAll(tmpdir) 789 if err != nil { 790 t.Error(err) 791 } 792 }() 793 794 dir0, err := os.Getwd() 795 if err != nil { 796 t.Fatal(err) 797 } 798 defer os.Chdir(dir0) 799 800 const src = ` 801 #include <stdint.h> 802 #include <windows.h> 803 804 uintptr_t cfunc() { 805 SetLastError(123); 806 } 807 ` 808 srcname := "nojack.c" 809 err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 810 if err != nil { 811 t.Fatal(err) 812 } 813 name := "nojack.dll" 814 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname) 815 cmd.Dir = tmpdir 816 out, err := cmd.CombinedOutput() 817 if err != nil { 818 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 819 } 820 dllpath := filepath.Join(tmpdir, name) 821 822 dll := syscall.MustLoadDLL(dllpath) 823 dll.MustFindProc("cfunc") 824 dll.Release() 825 826 // Get into the directory with the DLL we'll load by base name 827 // ("nojack.dll") Think of this as the user double-clicking an 828 // installer from their Downloads directory where a browser 829 // silently downloaded some malicious DLLs. 830 os.Chdir(tmpdir) 831 832 // First before we can load a DLL from the current directory, 833 // loading it only as "nojack.dll", without an absolute path. 834 delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly 835 dll, err = syscall.LoadDLL(name) 836 if err != nil { 837 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err) 838 } 839 dll.Release() 840 841 // And now verify that if we register it as a system32-only 842 // DLL, the implicit loading from the current directory no 843 // longer works. 844 sysdll.IsSystemDLL[name] = true 845 dll, err = syscall.LoadDLL(name) 846 if err == nil { 847 dll.Release() 848 if wantLoadLibraryEx() { 849 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err) 850 } 851 t.Skip("insecure load of DLL, but expected") 852 } 853 } 854 855 // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests. 856 func wantLoadLibraryEx() bool { 857 return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce" 858 } 859 860 func TestLoadLibraryEx(t *testing.T) { 861 use, have, flags := runtime.LoadLibraryExStatus() 862 if use { 863 return // success. 864 } 865 if wantLoadLibraryEx() { 866 t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)", 867 have, flags) 868 } 869 t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)", 870 have, flags) 871 }