github.com/lzhfromustc/gofuzz@v0.0.0-20211116160056-151b3108bbd1/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" 13 "math" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "reflect" 18 "runtime" 19 "strconv" 20 "strings" 21 "syscall" 22 "testing" 23 "unsafe" 24 ) 25 26 type DLL struct { 27 *syscall.DLL 28 t *testing.T 29 } 30 31 func GetDLL(t *testing.T, name string) *DLL { 32 d, e := syscall.LoadDLL(name) 33 if e != nil { 34 t.Fatal(e) 35 } 36 return &DLL{DLL: d, t: t} 37 } 38 39 func (d *DLL) Proc(name string) *syscall.Proc { 40 p, e := d.FindProc(name) 41 if e != nil { 42 d.t.Fatal(e) 43 } 44 return p 45 } 46 47 func TestStdCall(t *testing.T) { 48 type Rect struct { 49 left, top, right, bottom int32 50 } 51 res := Rect{} 52 expected := Rect{1, 1, 40, 60} 53 a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call( 54 uintptr(unsafe.Pointer(&res)), 55 uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})), 56 uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50}))) 57 if a != 1 || res.left != expected.left || 58 res.top != expected.top || 59 res.right != expected.right || 60 res.bottom != expected.bottom { 61 t.Error("stdcall USER32.UnionRect returns", a, "res=", res) 62 } 63 } 64 65 func Test64BitReturnStdCall(t *testing.T) { 66 67 const ( 68 VER_BUILDNUMBER = 0x0000004 69 VER_MAJORVERSION = 0x0000002 70 VER_MINORVERSION = 0x0000001 71 VER_PLATFORMID = 0x0000008 72 VER_PRODUCT_TYPE = 0x0000080 73 VER_SERVICEPACKMAJOR = 0x0000020 74 VER_SERVICEPACKMINOR = 0x0000010 75 VER_SUITENAME = 0x0000040 76 77 VER_EQUAL = 1 78 VER_GREATER = 2 79 VER_GREATER_EQUAL = 3 80 VER_LESS = 4 81 VER_LESS_EQUAL = 5 82 83 ERROR_OLD_WIN_VERSION syscall.Errno = 1150 84 ) 85 86 type OSVersionInfoEx struct { 87 OSVersionInfoSize uint32 88 MajorVersion uint32 89 MinorVersion uint32 90 BuildNumber uint32 91 PlatformId uint32 92 CSDVersion [128]uint16 93 ServicePackMajor uint16 94 ServicePackMinor uint16 95 SuiteMask uint16 96 ProductType byte 97 Reserve byte 98 } 99 100 d := GetDLL(t, "kernel32.dll") 101 102 var m1, m2 uintptr 103 VerSetConditionMask := d.Proc("VerSetConditionMask") 104 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL) 105 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL) 106 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL) 107 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL) 108 109 vi := OSVersionInfoEx{ 110 MajorVersion: 5, 111 MinorVersion: 1, 112 ServicePackMajor: 2, 113 ServicePackMinor: 0, 114 } 115 vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi)) 116 r, _, e2 := d.Proc("VerifyVersionInfoW").Call( 117 uintptr(unsafe.Pointer(&vi)), 118 VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR, 119 m1, m2) 120 if r == 0 && e2 != ERROR_OLD_WIN_VERSION { 121 t.Errorf("VerifyVersionInfo failed: %s", e2) 122 } 123 } 124 125 func TestCDecl(t *testing.T) { 126 var buf [50]byte 127 fmtp, _ := syscall.BytePtrFromString("%d %d %d") 128 a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call( 129 uintptr(unsafe.Pointer(&buf[0])), 130 uintptr(unsafe.Pointer(fmtp)), 131 1000, 2000, 3000) 132 if string(buf[:a]) != "1000 2000 3000" { 133 t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a]) 134 } 135 } 136 137 func TestEnumWindows(t *testing.T) { 138 d := GetDLL(t, "user32.dll") 139 isWindows := d.Proc("IsWindow") 140 counter := 0 141 cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr { 142 if lparam != 888 { 143 t.Error("lparam was not passed to callback") 144 } 145 b, _, _ := isWindows.Call(uintptr(hwnd)) 146 if b == 0 { 147 t.Error("USER32.IsWindow returns FALSE") 148 } 149 counter++ 150 return 1 // continue enumeration 151 }) 152 a, _, _ := d.Proc("EnumWindows").Call(cb, 888) 153 if a == 0 { 154 t.Error("USER32.EnumWindows returns FALSE") 155 } 156 if counter == 0 { 157 t.Error("Callback has been never called or your have no windows") 158 } 159 } 160 161 func callback(timeFormatString unsafe.Pointer, lparam uintptr) uintptr { 162 (*(*func())(unsafe.Pointer(&lparam)))() 163 return 0 // stop enumeration 164 } 165 166 // nestedCall calls into Windows, back into Go, and finally to f. 167 func nestedCall(t *testing.T, f func()) { 168 c := syscall.NewCallback(callback) 169 d := GetDLL(t, "kernel32.dll") 170 defer d.Release() 171 const LOCALE_NAME_USER_DEFAULT = 0 172 d.Proc("EnumTimeFormatsEx").Call(c, LOCALE_NAME_USER_DEFAULT, 0, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f)))) 173 } 174 175 func TestCallback(t *testing.T) { 176 var x = false 177 nestedCall(t, func() { x = true }) 178 if !x { 179 t.Fatal("nestedCall did not call func") 180 } 181 } 182 183 func TestCallbackGC(t *testing.T) { 184 nestedCall(t, runtime.GC) 185 } 186 187 func TestCallbackPanicLocked(t *testing.T) { 188 runtime.LockOSThread() 189 defer runtime.UnlockOSThread() 190 191 if !runtime.LockedOSThread() { 192 t.Fatal("runtime.LockOSThread didn't") 193 } 194 defer func() { 195 s := recover() 196 if s == nil { 197 t.Fatal("did not panic") 198 } 199 if s.(string) != "callback panic" { 200 t.Fatal("wrong panic:", s) 201 } 202 if !runtime.LockedOSThread() { 203 t.Fatal("lost lock on OS thread after panic") 204 } 205 }() 206 nestedCall(t, func() { panic("callback panic") }) 207 panic("nestedCall returned") 208 } 209 210 func TestCallbackPanic(t *testing.T) { 211 // Make sure panic during callback unwinds properly. 212 if runtime.LockedOSThread() { 213 t.Fatal("locked OS thread on entry to TestCallbackPanic") 214 } 215 defer func() { 216 s := recover() 217 if s == nil { 218 t.Fatal("did not panic") 219 } 220 if s.(string) != "callback panic" { 221 t.Fatal("wrong panic:", s) 222 } 223 if runtime.LockedOSThread() { 224 t.Fatal("locked OS thread on exit from TestCallbackPanic") 225 } 226 }() 227 nestedCall(t, func() { panic("callback panic") }) 228 panic("nestedCall returned") 229 } 230 231 func TestCallbackPanicLoop(t *testing.T) { 232 // Make sure we don't blow out m->g0 stack. 233 for i := 0; i < 100000; i++ { 234 TestCallbackPanic(t) 235 } 236 } 237 238 func TestBlockingCallback(t *testing.T) { 239 c := make(chan int) 240 go func() { 241 for i := 0; i < 10; i++ { 242 c <- <-c 243 } 244 }() 245 nestedCall(t, func() { 246 for i := 0; i < 10; i++ { 247 c <- i 248 if j := <-c; j != i { 249 t.Errorf("out of sync %d != %d", j, i) 250 } 251 } 252 }) 253 } 254 255 func TestCallbackInAnotherThread(t *testing.T) { 256 d := GetDLL(t, "kernel32.dll") 257 258 f := func(p uintptr) uintptr { 259 return p 260 } 261 r, _, err := d.Proc("CreateThread").Call(0, 0, syscall.NewCallback(f), 123, 0, 0) 262 if r == 0 { 263 t.Fatalf("CreateThread failed: %v", err) 264 } 265 h := syscall.Handle(r) 266 defer syscall.CloseHandle(h) 267 268 switch s, err := syscall.WaitForSingleObject(h, 100); s { 269 case syscall.WAIT_OBJECT_0: 270 break 271 case syscall.WAIT_TIMEOUT: 272 t.Fatal("timeout waiting for thread to exit") 273 case syscall.WAIT_FAILED: 274 t.Fatalf("WaitForSingleObject failed: %v", err) 275 default: 276 t.Fatalf("WaitForSingleObject returns unexpected value %v", s) 277 } 278 279 var ec uint32 280 r, _, err = d.Proc("GetExitCodeThread").Call(uintptr(h), uintptr(unsafe.Pointer(&ec))) 281 if r == 0 { 282 t.Fatalf("GetExitCodeThread failed: %v", err) 283 } 284 if ec != 123 { 285 t.Fatalf("expected 123, but got %d", ec) 286 } 287 } 288 289 type cbFunc struct { 290 goFunc interface{} 291 } 292 293 func (f cbFunc) cName(cdecl bool) string { 294 name := "stdcall" 295 if cdecl { 296 name = "cdecl" 297 } 298 t := reflect.TypeOf(f.goFunc) 299 for i := 0; i < t.NumIn(); i++ { 300 name += "_" + t.In(i).Name() 301 } 302 return name 303 } 304 305 func (f cbFunc) cSrc(w io.Writer, cdecl bool) { 306 // Construct a C function that takes a callback with 307 // f.goFunc's signature, and calls it with integers 1..N. 308 funcname := f.cName(cdecl) 309 attr := "__stdcall" 310 if cdecl { 311 attr = "__cdecl" 312 } 313 typename := "t" + funcname 314 t := reflect.TypeOf(f.goFunc) 315 cTypes := make([]string, t.NumIn()) 316 cArgs := make([]string, t.NumIn()) 317 for i := range cTypes { 318 // We included stdint.h, so this works for all sized 319 // integer types, and uint8Pair_t. 320 cTypes[i] = t.In(i).Name() + "_t" 321 if t.In(i).Name() == "uint8Pair" { 322 cArgs[i] = fmt.Sprintf("(uint8Pair_t){%d,1}", i) 323 } else { 324 cArgs[i] = fmt.Sprintf("%d", i+1) 325 } 326 } 327 fmt.Fprintf(w, ` 328 typedef uintptr_t %s (*%s)(%s); 329 uintptr_t %s(%s f) { 330 return f(%s); 331 } 332 `, attr, typename, strings.Join(cTypes, ","), funcname, typename, strings.Join(cArgs, ",")) 333 } 334 335 func (f cbFunc) testOne(t *testing.T, dll *syscall.DLL, cdecl bool, cb uintptr) { 336 r1, _, _ := dll.MustFindProc(f.cName(cdecl)).Call(cb) 337 338 want := 0 339 for i := 0; i < reflect.TypeOf(f.goFunc).NumIn(); i++ { 340 want += i + 1 341 } 342 if int(r1) != want { 343 t.Errorf("wanted result %d; got %d", want, r1) 344 } 345 } 346 347 type uint8Pair struct{ x, y uint8 } 348 349 var cbFuncs = []cbFunc{ 350 {func(i1, i2 uintptr) uintptr { 351 return i1 + i2 352 }}, 353 {func(i1, i2, i3 uintptr) uintptr { 354 return i1 + i2 + i3 355 }}, 356 {func(i1, i2, i3, i4 uintptr) uintptr { 357 return i1 + i2 + i3 + i4 358 }}, 359 {func(i1, i2, i3, i4, i5 uintptr) uintptr { 360 return i1 + i2 + i3 + i4 + i5 361 }}, 362 {func(i1, i2, i3, i4, i5, i6 uintptr) uintptr { 363 return i1 + i2 + i3 + i4 + i5 + i6 364 }}, 365 {func(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr { 366 return i1 + i2 + i3 + i4 + i5 + i6 + i7 367 }}, 368 {func(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr { 369 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 370 }}, 371 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr { 372 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 373 }}, 374 375 // Non-uintptr parameters. 376 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint8) uintptr { 377 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9) 378 }}, 379 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint16) uintptr { 380 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9) 381 }}, 382 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 int8) uintptr { 383 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9) 384 }}, 385 {func(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr { 386 return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5 387 }}, 388 {func(i1, i2, i3, i4, i5 uint8Pair) uintptr { 389 return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y) 390 }}, 391 } 392 393 type cbDLL struct { 394 name string 395 buildArgs func(out, src string) []string 396 } 397 398 func (d *cbDLL) makeSrc(t *testing.T, path string) { 399 f, err := os.Create(path) 400 if err != nil { 401 t.Fatalf("failed to create source file: %v", err) 402 } 403 defer f.Close() 404 405 fmt.Fprint(f, ` 406 #include <stdint.h> 407 typedef struct { uint8_t x, y; } uint8Pair_t; 408 `) 409 for _, cbf := range cbFuncs { 410 cbf.cSrc(f, false) 411 cbf.cSrc(f, true) 412 } 413 } 414 415 func (d *cbDLL) build(t *testing.T, dir string) string { 416 srcname := d.name + ".c" 417 d.makeSrc(t, filepath.Join(dir, srcname)) 418 outname := d.name + ".dll" 419 args := d.buildArgs(outname, srcname) 420 cmd := exec.Command(args[0], args[1:]...) 421 cmd.Dir = dir 422 out, err := cmd.CombinedOutput() 423 if err != nil { 424 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 425 } 426 return filepath.Join(dir, outname) 427 } 428 429 var cbDLLs = []cbDLL{ 430 { 431 "test", 432 func(out, src string) []string { 433 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src} 434 }, 435 }, 436 { 437 "testO2", 438 func(out, src string) []string { 439 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src} 440 }, 441 }, 442 } 443 444 func TestStdcallAndCDeclCallbacks(t *testing.T) { 445 if _, err := exec.LookPath("gcc"); err != nil { 446 t.Skip("skipping test: gcc is missing") 447 } 448 tmp, err := os.MkdirTemp("", "TestCDeclCallback") 449 if err != nil { 450 t.Fatal("TempDir failed: ", err) 451 } 452 defer os.RemoveAll(tmp) 453 454 for _, dll := range cbDLLs { 455 t.Run(dll.name, func(t *testing.T) { 456 dllPath := dll.build(t, tmp) 457 dll := syscall.MustLoadDLL(dllPath) 458 defer dll.Release() 459 for _, cbf := range cbFuncs { 460 t.Run(cbf.cName(false), func(t *testing.T) { 461 stdcall := syscall.NewCallback(cbf.goFunc) 462 cbf.testOne(t, dll, false, stdcall) 463 }) 464 t.Run(cbf.cName(true), func(t *testing.T) { 465 cdecl := syscall.NewCallbackCDecl(cbf.goFunc) 466 cbf.testOne(t, dll, true, cdecl) 467 }) 468 } 469 }) 470 } 471 } 472 473 func TestRegisterClass(t *testing.T) { 474 kernel32 := GetDLL(t, "kernel32.dll") 475 user32 := GetDLL(t, "user32.dll") 476 mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0) 477 cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) { 478 t.Fatal("callback should never get called") 479 return 0 480 }) 481 type Wndclassex struct { 482 Size uint32 483 Style uint32 484 WndProc uintptr 485 ClsExtra int32 486 WndExtra int32 487 Instance syscall.Handle 488 Icon syscall.Handle 489 Cursor syscall.Handle 490 Background syscall.Handle 491 MenuName *uint16 492 ClassName *uint16 493 IconSm syscall.Handle 494 } 495 name := syscall.StringToUTF16Ptr("test_window") 496 wc := Wndclassex{ 497 WndProc: cb, 498 Instance: syscall.Handle(mh), 499 ClassName: name, 500 } 501 wc.Size = uint32(unsafe.Sizeof(wc)) 502 a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc))) 503 if a == 0 { 504 t.Fatalf("RegisterClassEx failed: %v", err) 505 } 506 r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0) 507 if r == 0 { 508 t.Fatalf("UnregisterClass failed: %v", err) 509 } 510 } 511 512 func TestOutputDebugString(t *testing.T) { 513 d := GetDLL(t, "kernel32.dll") 514 p := syscall.StringToUTF16Ptr("testing OutputDebugString") 515 d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p))) 516 } 517 518 func TestRaiseException(t *testing.T) { 519 o := runTestProg(t, "testprog", "RaiseException") 520 if strings.Contains(o, "RaiseException should not return") { 521 t.Fatalf("RaiseException did not crash program: %v", o) 522 } 523 if !strings.Contains(o, "Exception 0xbad") { 524 t.Fatalf("No stack trace: %v", o) 525 } 526 } 527 528 func TestZeroDivisionException(t *testing.T) { 529 o := runTestProg(t, "testprog", "ZeroDivisionException") 530 if !strings.Contains(o, "panic: runtime error: integer divide by zero") { 531 t.Fatalf("No stack trace: %v", o) 532 } 533 } 534 535 func TestWERDialogue(t *testing.T) { 536 if os.Getenv("TESTING_WER_DIALOGUE") == "1" { 537 defer os.Exit(0) 538 539 *runtime.TestingWER = true 540 const EXCEPTION_NONCONTINUABLE = 1 541 mod := syscall.MustLoadDLL("kernel32.dll") 542 proc := mod.MustFindProc("RaiseException") 543 proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0) 544 println("RaiseException should not return") 545 return 546 } 547 cmd := exec.Command(os.Args[0], "-test.run=TestWERDialogue") 548 cmd.Env = []string{"TESTING_WER_DIALOGUE=1"} 549 // Child process should not open WER dialogue, but return immediately instead. 550 cmd.CombinedOutput() 551 } 552 553 func TestWindowsStackMemory(t *testing.T) { 554 o := runTestProg(t, "testprog", "StackMemory") 555 stackUsage, err := strconv.Atoi(o) 556 if err != nil { 557 t.Fatalf("Failed to read stack usage: %v", err) 558 } 559 if expected, got := 100<<10, stackUsage; got > expected { 560 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got) 561 } 562 } 563 564 var used byte 565 566 func use(buf []byte) { 567 for _, c := range buf { 568 used += c 569 } 570 } 571 572 func forceStackCopy() (r int) { 573 var f func(int) int 574 f = func(i int) int { 575 var buf [256]byte 576 use(buf[:]) 577 if i == 0 { 578 return 0 579 } 580 return i + f(i-1) 581 } 582 r = f(128) 583 return 584 } 585 586 func TestReturnAfterStackGrowInCallback(t *testing.T) { 587 if _, err := exec.LookPath("gcc"); err != nil { 588 t.Skip("skipping test: gcc is missing") 589 } 590 591 const src = ` 592 #include <stdint.h> 593 #include <windows.h> 594 595 typedef uintptr_t __stdcall (*callback)(uintptr_t); 596 597 uintptr_t cfunc(callback f, uintptr_t n) { 598 uintptr_t r; 599 r = f(n); 600 SetLastError(333); 601 return r; 602 } 603 ` 604 tmpdir, err := os.MkdirTemp("", "TestReturnAfterStackGrowInCallback") 605 if err != nil { 606 t.Fatal("TempDir failed: ", err) 607 } 608 defer os.RemoveAll(tmpdir) 609 610 srcname := "mydll.c" 611 err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 612 if err != nil { 613 t.Fatal(err) 614 } 615 outname := "mydll.dll" 616 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 617 cmd.Dir = tmpdir 618 out, err := cmd.CombinedOutput() 619 if err != nil { 620 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 621 } 622 dllpath := filepath.Join(tmpdir, outname) 623 624 dll := syscall.MustLoadDLL(dllpath) 625 defer dll.Release() 626 627 proc := dll.MustFindProc("cfunc") 628 629 cb := syscall.NewCallback(func(n uintptr) uintptr { 630 forceStackCopy() 631 return n 632 }) 633 634 // Use a new goroutine so that we get a small stack. 635 type result struct { 636 r uintptr 637 err syscall.Errno 638 } 639 want := result{ 640 // Make it large enough to test issue #29331. 641 r: (^uintptr(0)) >> 24, 642 err: 333, 643 } 644 c := make(chan result) 645 go func() { 646 r, _, err := proc.Call(cb, want.r) 647 c <- result{r, err.(syscall.Errno)} 648 }() 649 if got := <-c; got != want { 650 t.Errorf("got %d want %d", got, want) 651 } 652 } 653 654 func TestFloatArgs(t *testing.T) { 655 if _, err := exec.LookPath("gcc"); err != nil { 656 t.Skip("skipping test: gcc is missing") 657 } 658 if runtime.GOARCH != "amd64" { 659 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH) 660 } 661 662 const src = ` 663 #include <stdint.h> 664 #include <windows.h> 665 666 uintptr_t cfunc(uintptr_t a, double b, float c, double d) { 667 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 668 return 1; 669 } 670 return 0; 671 } 672 ` 673 tmpdir, err := os.MkdirTemp("", "TestFloatArgs") 674 if err != nil { 675 t.Fatal("TempDir failed: ", err) 676 } 677 defer os.RemoveAll(tmpdir) 678 679 srcname := "mydll.c" 680 err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 681 if err != nil { 682 t.Fatal(err) 683 } 684 outname := "mydll.dll" 685 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 686 cmd.Dir = tmpdir 687 out, err := cmd.CombinedOutput() 688 if err != nil { 689 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 690 } 691 dllpath := filepath.Join(tmpdir, outname) 692 693 dll := syscall.MustLoadDLL(dllpath) 694 defer dll.Release() 695 696 proc := dll.MustFindProc("cfunc") 697 698 r, _, err := proc.Call( 699 1, 700 uintptr(math.Float64bits(2.2)), 701 uintptr(math.Float32bits(3.3)), 702 uintptr(math.Float64bits(4.4e44)), 703 ) 704 if r != 1 { 705 t.Errorf("got %d want 1 (err=%v)", r, err) 706 } 707 } 708 709 func TestFloatReturn(t *testing.T) { 710 if _, err := exec.LookPath("gcc"); err != nil { 711 t.Skip("skipping test: gcc is missing") 712 } 713 if runtime.GOARCH != "amd64" { 714 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH) 715 } 716 717 const src = ` 718 #include <stdint.h> 719 #include <windows.h> 720 721 float cfuncFloat(uintptr_t a, double b, float c, double d) { 722 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 723 return 1.5f; 724 } 725 return 0; 726 } 727 728 double cfuncDouble(uintptr_t a, double b, float c, double d) { 729 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 730 return 2.5; 731 } 732 return 0; 733 } 734 ` 735 tmpdir, err := os.MkdirTemp("", "TestFloatReturn") 736 if err != nil { 737 t.Fatal("TempDir failed: ", err) 738 } 739 defer os.RemoveAll(tmpdir) 740 741 srcname := "mydll.c" 742 err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 743 if err != nil { 744 t.Fatal(err) 745 } 746 outname := "mydll.dll" 747 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 748 cmd.Dir = tmpdir 749 out, err := cmd.CombinedOutput() 750 if err != nil { 751 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 752 } 753 dllpath := filepath.Join(tmpdir, outname) 754 755 dll := syscall.MustLoadDLL(dllpath) 756 defer dll.Release() 757 758 proc := dll.MustFindProc("cfuncFloat") 759 760 _, r, err := proc.Call( 761 1, 762 uintptr(math.Float64bits(2.2)), 763 uintptr(math.Float32bits(3.3)), 764 uintptr(math.Float64bits(4.4e44)), 765 ) 766 fr := math.Float32frombits(uint32(r)) 767 if fr != 1.5 { 768 t.Errorf("got %f want 1.5 (err=%v)", fr, err) 769 } 770 771 proc = dll.MustFindProc("cfuncDouble") 772 773 _, r, err = proc.Call( 774 1, 775 uintptr(math.Float64bits(2.2)), 776 uintptr(math.Float32bits(3.3)), 777 uintptr(math.Float64bits(4.4e44)), 778 ) 779 dr := math.Float64frombits(uint64(r)) 780 if dr != 2.5 { 781 t.Errorf("got %f want 2.5 (err=%v)", dr, err) 782 } 783 } 784 785 func TestTimeBeginPeriod(t *testing.T) { 786 const TIMERR_NOERROR = 0 787 if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR { 788 t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue) 789 } 790 } 791 792 // removeOneCPU removes one (any) cpu from affinity mask. 793 // It returns new affinity mask. 794 func removeOneCPU(mask uintptr) (uintptr, error) { 795 if mask == 0 { 796 return 0, fmt.Errorf("cpu affinity mask is empty") 797 } 798 maskbits := int(unsafe.Sizeof(mask) * 8) 799 for i := 0; i < maskbits; i++ { 800 newmask := mask & ^(1 << uint(i)) 801 if newmask != mask { 802 return newmask, nil 803 } 804 805 } 806 panic("not reached") 807 } 808 809 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error { 810 _OpenThread := kernel32.MustFindProc("OpenThread") 811 _ResumeThread := kernel32.MustFindProc("ResumeThread") 812 _Thread32First := kernel32.MustFindProc("Thread32First") 813 _Thread32Next := kernel32.MustFindProc("Thread32Next") 814 815 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0) 816 if err != nil { 817 return err 818 } 819 defer syscall.CloseHandle(snapshot) 820 821 const _THREAD_SUSPEND_RESUME = 0x0002 822 823 type ThreadEntry32 struct { 824 Size uint32 825 tUsage uint32 826 ThreadID uint32 827 OwnerProcessID uint32 828 BasePri int32 829 DeltaPri int32 830 Flags uint32 831 } 832 833 var te ThreadEntry32 834 te.Size = uint32(unsafe.Sizeof(te)) 835 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 836 if ret == 0 { 837 return err 838 } 839 for te.OwnerProcessID != uint32(childpid) { 840 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 841 if ret == 0 { 842 return err 843 } 844 } 845 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID)) 846 if h == 0 { 847 return err 848 } 849 defer syscall.Close(syscall.Handle(h)) 850 851 ret, _, err = _ResumeThread.Call(h) 852 if ret == 0xffffffff { 853 return err 854 } 855 return nil 856 } 857 858 func TestNumCPU(t *testing.T) { 859 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 860 // in child process 861 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU()) 862 os.Exit(0) 863 } 864 865 switch n := runtime.NumberOfProcessors(); { 866 case n < 1: 867 t.Fatalf("system cannot have %d cpu(s)", n) 868 case n == 1: 869 if runtime.NumCPU() != 1 { 870 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU()) 871 } 872 return 873 } 874 875 const ( 876 _CREATE_SUSPENDED = 0x00000004 877 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff 878 ) 879 880 kernel32 := syscall.MustLoadDLL("kernel32.dll") 881 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask") 882 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask") 883 884 cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU") 885 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") 886 var buf bytes.Buffer 887 cmd.Stdout = &buf 888 cmd.Stderr = &buf 889 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED} 890 err := cmd.Start() 891 if err != nil { 892 t.Fatal(err) 893 } 894 defer func() { 895 err = cmd.Wait() 896 childOutput := string(buf.Bytes()) 897 if err != nil { 898 t.Fatalf("child failed: %v: %v", err, childOutput) 899 } 900 // removeOneCPU should have decreased child cpu count by 1 901 want := fmt.Sprintf("%d", runtime.NumCPU()-1) 902 if childOutput != want { 903 t.Fatalf("child output: want %q, got %q", want, childOutput) 904 } 905 }() 906 907 defer func() { 908 err = resumeChildThread(kernel32, cmd.Process.Pid) 909 if err != nil { 910 t.Fatal(err) 911 } 912 }() 913 914 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid)) 915 if err != nil { 916 t.Fatal(err) 917 } 918 defer syscall.CloseHandle(ph) 919 920 var mask, sysmask uintptr 921 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 922 if ret == 0 { 923 t.Fatal(err) 924 } 925 926 newmask, err := removeOneCPU(mask) 927 if err != nil { 928 t.Fatal(err) 929 } 930 931 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask) 932 if ret == 0 { 933 t.Fatal(err) 934 } 935 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 936 if ret == 0 { 937 t.Fatal(err) 938 } 939 if newmask != mask { 940 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask) 941 } 942 } 943 944 // See Issue 14959 945 func TestDLLPreloadMitigation(t *testing.T) { 946 if _, err := exec.LookPath("gcc"); err != nil { 947 t.Skip("skipping test: gcc is missing") 948 } 949 950 tmpdir, err := os.MkdirTemp("", "TestDLLPreloadMitigation") 951 if err != nil { 952 t.Fatal("TempDir failed: ", err) 953 } 954 defer func() { 955 err := os.RemoveAll(tmpdir) 956 if err != nil { 957 t.Error(err) 958 } 959 }() 960 961 dir0, err := os.Getwd() 962 if err != nil { 963 t.Fatal(err) 964 } 965 defer os.Chdir(dir0) 966 967 const src = ` 968 #include <stdint.h> 969 #include <windows.h> 970 971 uintptr_t cfunc(void) { 972 SetLastError(123); 973 return 0; 974 } 975 ` 976 srcname := "nojack.c" 977 err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 978 if err != nil { 979 t.Fatal(err) 980 } 981 name := "nojack.dll" 982 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname) 983 cmd.Dir = tmpdir 984 out, err := cmd.CombinedOutput() 985 if err != nil { 986 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 987 } 988 dllpath := filepath.Join(tmpdir, name) 989 990 dll := syscall.MustLoadDLL(dllpath) 991 dll.MustFindProc("cfunc") 992 dll.Release() 993 994 // Get into the directory with the DLL we'll load by base name 995 // ("nojack.dll") Think of this as the user double-clicking an 996 // installer from their Downloads directory where a browser 997 // silently downloaded some malicious DLLs. 998 os.Chdir(tmpdir) 999 1000 // First before we can load a DLL from the current directory, 1001 // loading it only as "nojack.dll", without an absolute path. 1002 delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly 1003 dll, err = syscall.LoadDLL(name) 1004 if err != nil { 1005 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err) 1006 } 1007 dll.Release() 1008 1009 // And now verify that if we register it as a system32-only 1010 // DLL, the implicit loading from the current directory no 1011 // longer works. 1012 sysdll.IsSystemDLL[name] = true 1013 dll, err = syscall.LoadDLL(name) 1014 if err == nil { 1015 dll.Release() 1016 if wantLoadLibraryEx() { 1017 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err) 1018 } 1019 t.Skip("insecure load of DLL, but expected") 1020 } 1021 } 1022 1023 // Test that C code called via a DLL can use large Windows thread 1024 // stacks and call back in to Go without crashing. See issue #20975. 1025 // 1026 // See also TestBigStackCallbackCgo. 1027 func TestBigStackCallbackSyscall(t *testing.T) { 1028 if _, err := exec.LookPath("gcc"); err != nil { 1029 t.Skip("skipping test: gcc is missing") 1030 } 1031 1032 srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c") 1033 if err != nil { 1034 t.Fatal("Abs failed: ", err) 1035 } 1036 1037 tmpdir, err := os.MkdirTemp("", "TestBigStackCallback") 1038 if err != nil { 1039 t.Fatal("TempDir failed: ", err) 1040 } 1041 defer os.RemoveAll(tmpdir) 1042 1043 outname := "mydll.dll" 1044 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 1045 cmd.Dir = tmpdir 1046 out, err := cmd.CombinedOutput() 1047 if err != nil { 1048 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 1049 } 1050 dllpath := filepath.Join(tmpdir, outname) 1051 1052 dll := syscall.MustLoadDLL(dllpath) 1053 defer dll.Release() 1054 1055 var ok bool 1056 proc := dll.MustFindProc("bigStack") 1057 cb := syscall.NewCallback(func() uintptr { 1058 // Do something interesting to force stack checks. 1059 forceStackCopy() 1060 ok = true 1061 return 0 1062 }) 1063 proc.Call(cb) 1064 if !ok { 1065 t.Fatalf("callback not called") 1066 } 1067 } 1068 1069 // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests. 1070 func wantLoadLibraryEx() bool { 1071 return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce" 1072 } 1073 1074 func TestLoadLibraryEx(t *testing.T) { 1075 use, have, flags := runtime.LoadLibraryExStatus() 1076 if use { 1077 return // success. 1078 } 1079 if wantLoadLibraryEx() { 1080 t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)", 1081 have, flags) 1082 } 1083 t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)", 1084 have, flags) 1085 } 1086 1087 var ( 1088 modwinmm = syscall.NewLazyDLL("winmm.dll") 1089 modkernel32 = syscall.NewLazyDLL("kernel32.dll") 1090 1091 procCreateEvent = modkernel32.NewProc("CreateEventW") 1092 procSetEvent = modkernel32.NewProc("SetEvent") 1093 ) 1094 1095 func createEvent() (syscall.Handle, error) { 1096 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0) 1097 if r0 == 0 { 1098 return 0, syscall.Errno(e0) 1099 } 1100 return syscall.Handle(r0), nil 1101 } 1102 1103 func setEvent(h syscall.Handle) error { 1104 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0) 1105 if r0 == 0 { 1106 return syscall.Errno(e0) 1107 } 1108 return nil 1109 } 1110 1111 func BenchmarkChanToSyscallPing(b *testing.B) { 1112 n := b.N 1113 ch := make(chan int) 1114 event, err := createEvent() 1115 if err != nil { 1116 b.Fatal(err) 1117 } 1118 go func() { 1119 for i := 0; i < n; i++ { 1120 syscall.WaitForSingleObject(event, syscall.INFINITE) 1121 ch <- 1 1122 } 1123 }() 1124 for i := 0; i < n; i++ { 1125 err := setEvent(event) 1126 if err != nil { 1127 b.Fatal(err) 1128 } 1129 <-ch 1130 } 1131 } 1132 1133 func BenchmarkSyscallToSyscallPing(b *testing.B) { 1134 n := b.N 1135 event1, err := createEvent() 1136 if err != nil { 1137 b.Fatal(err) 1138 } 1139 event2, err := createEvent() 1140 if err != nil { 1141 b.Fatal(err) 1142 } 1143 go func() { 1144 for i := 0; i < n; i++ { 1145 syscall.WaitForSingleObject(event1, syscall.INFINITE) 1146 if err := setEvent(event2); err != nil { 1147 b.Errorf("Set event failed: %v", err) 1148 return 1149 } 1150 } 1151 }() 1152 for i := 0; i < n; i++ { 1153 if err := setEvent(event1); err != nil { 1154 b.Fatal(err) 1155 } 1156 if b.Failed() { 1157 break 1158 } 1159 syscall.WaitForSingleObject(event2, syscall.INFINITE) 1160 } 1161 } 1162 1163 func BenchmarkChanToChanPing(b *testing.B) { 1164 n := b.N 1165 ch1 := make(chan int) 1166 ch2 := make(chan int) 1167 go func() { 1168 for i := 0; i < n; i++ { 1169 <-ch1 1170 ch2 <- 1 1171 } 1172 }() 1173 for i := 0; i < n; i++ { 1174 ch1 <- 1 1175 <-ch2 1176 } 1177 } 1178 1179 func BenchmarkOsYield(b *testing.B) { 1180 for i := 0; i < b.N; i++ { 1181 runtime.OsYield() 1182 } 1183 } 1184 1185 func BenchmarkRunningGoProgram(b *testing.B) { 1186 tmpdir, err := os.MkdirTemp("", "BenchmarkRunningGoProgram") 1187 if err != nil { 1188 b.Fatal(err) 1189 } 1190 defer os.RemoveAll(tmpdir) 1191 1192 src := filepath.Join(tmpdir, "main.go") 1193 err = os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666) 1194 if err != nil { 1195 b.Fatal(err) 1196 } 1197 1198 exe := filepath.Join(tmpdir, "main.exe") 1199 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src) 1200 cmd.Dir = tmpdir 1201 out, err := cmd.CombinedOutput() 1202 if err != nil { 1203 b.Fatalf("building main.exe failed: %v\n%s", err, out) 1204 } 1205 1206 b.ResetTimer() 1207 for i := 0; i < b.N; i++ { 1208 cmd := exec.Command(exe) 1209 out, err := cmd.CombinedOutput() 1210 if err != nil { 1211 b.Fatalf("running main.exe failed: %v\n%s", err, out) 1212 } 1213 } 1214 } 1215 1216 const benchmarkRunningGoProgram = ` 1217 package main 1218 1219 import _ "os" // average Go program will use "os" package, do the same here 1220 1221 func main() { 1222 } 1223 `