golang.org/x/sys@v0.20.1-0.20240517151509-673e0f94c16d/windows/syscall_windows_test.go (about) 1 // Copyright 2012 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 windows_test 6 7 import ( 8 "bufio" 9 "bytes" 10 "debug/pe" 11 "errors" 12 "fmt" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "runtime" 17 "strconv" 18 "strings" 19 "syscall" 20 "testing" 21 "time" 22 "unicode/utf8" 23 "unsafe" 24 25 "golang.org/x/sys/windows" 26 ) 27 28 func TestWin32finddata(t *testing.T) { 29 path := filepath.Join(t.TempDir(), "long_name.and_extension") 30 f, err := os.Create(path) 31 if err != nil { 32 t.Fatalf("failed to create %v: %v", path, err) 33 } 34 f.Close() 35 36 type X struct { 37 fd windows.Win32finddata 38 got byte 39 pad [10]byte // to protect ourselves 40 41 } 42 var want byte = 2 // it is unlikely to have this character in the filename 43 x := X{got: want} 44 45 pathp, _ := windows.UTF16PtrFromString(path) 46 h, err := windows.FindFirstFile(pathp, &(x.fd)) 47 if err != nil { 48 t.Fatalf("FindFirstFile failed: %v", err) 49 } 50 err = windows.FindClose(h) 51 if err != nil { 52 t.Fatalf("FindClose failed: %v", err) 53 } 54 55 if x.got != want { 56 t.Fatalf("memory corruption: want=%d got=%d", want, x.got) 57 } 58 } 59 60 func TestFormatMessage(t *testing.T) { 61 dll := windows.MustLoadDLL("netevent.dll") 62 63 const TITLE_SC_MESSAGE_BOX uint32 = 0xC0001B75 64 const flags uint32 = syscall.FORMAT_MESSAGE_FROM_HMODULE | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS 65 buf := make([]uint16, 300) 66 _, err := windows.FormatMessage(flags, uintptr(dll.Handle), TITLE_SC_MESSAGE_BOX, 0, buf, nil) 67 if err != nil { 68 t.Fatalf("FormatMessage for handle=%x and errno=%x failed: %v", dll.Handle, TITLE_SC_MESSAGE_BOX, err) 69 } 70 } 71 72 func abort(funcname string, err error) { 73 panic(funcname + " failed: " + err.Error()) 74 } 75 76 func ExampleLoadLibrary() { 77 h, err := windows.LoadLibrary("kernel32.dll") 78 if err != nil { 79 abort("LoadLibrary", err) 80 } 81 defer windows.FreeLibrary(h) 82 proc, err := windows.GetProcAddress(h, "GetVersion") 83 if err != nil { 84 abort("GetProcAddress", err) 85 } 86 r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0) 87 major := byte(r) 88 minor := uint8(r >> 8) 89 build := uint16(r >> 16) 90 print("windows version ", major, ".", minor, " (Build ", build, ")\n") 91 } 92 93 func TestTOKEN_ALL_ACCESS(t *testing.T) { 94 if windows.TOKEN_ALL_ACCESS != 0xF01FF { 95 t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", windows.TOKEN_ALL_ACCESS) 96 } 97 } 98 99 func TestCreateWellKnownSid(t *testing.T) { 100 sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid) 101 if err != nil { 102 t.Fatalf("Unable to create well known sid for administrators: %v", err) 103 } 104 if got, want := sid.String(), "S-1-5-32-544"; got != want { 105 t.Fatalf("Builtin Administrators SID = %s, want %s", got, want) 106 } 107 } 108 109 func TestPseudoTokens(t *testing.T) { 110 version, err := windows.GetVersion() 111 if err != nil { 112 t.Fatal(err) 113 } 114 if ((version&0xffff)>>8)|((version&0xff)<<8) < 0x0602 { 115 return 116 } 117 118 realProcessToken, err := windows.OpenCurrentProcessToken() 119 if err != nil { 120 t.Fatal(err) 121 } 122 defer realProcessToken.Close() 123 realProcessUser, err := realProcessToken.GetTokenUser() 124 if err != nil { 125 t.Fatal(err) 126 } 127 128 pseudoProcessToken := windows.GetCurrentProcessToken() 129 pseudoProcessUser, err := pseudoProcessToken.GetTokenUser() 130 if err != nil { 131 t.Fatal(err) 132 } 133 if !windows.EqualSid(realProcessUser.User.Sid, pseudoProcessUser.User.Sid) { 134 t.Fatal("The real process token does not have the same as the pseudo process token") 135 } 136 137 runtime.LockOSThread() 138 defer runtime.UnlockOSThread() 139 140 err = windows.RevertToSelf() 141 if err != nil { 142 t.Fatal(err) 143 } 144 145 pseudoThreadToken := windows.GetCurrentThreadToken() 146 _, err = pseudoThreadToken.GetTokenUser() 147 if err != windows.ERROR_NO_TOKEN { 148 t.Fatal("Expected an empty thread token") 149 } 150 pseudoThreadEffectiveToken := windows.GetCurrentThreadEffectiveToken() 151 pseudoThreadEffectiveUser, err := pseudoThreadEffectiveToken.GetTokenUser() 152 if err != nil { 153 t.Fatal(nil) 154 } 155 if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadEffectiveUser.User.Sid) { 156 t.Fatal("The real process token does not have the same as the pseudo thread effective token, even though we aren't impersonating") 157 } 158 159 err = windows.ImpersonateSelf(windows.SecurityImpersonation) 160 if err != nil { 161 t.Fatal(err) 162 } 163 defer windows.RevertToSelf() 164 pseudoThreadUser, err := pseudoThreadToken.GetTokenUser() 165 if err != nil { 166 t.Fatal(err) 167 } 168 if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadUser.User.Sid) { 169 t.Fatal("The real process token does not have the same as the pseudo thread token after impersonating self") 170 } 171 } 172 173 func TestGUID(t *testing.T) { 174 guid, err := windows.GenerateGUID() 175 if err != nil { 176 t.Fatal(err) 177 } 178 if guid.Data1 == 0 && guid.Data2 == 0 && guid.Data3 == 0 && guid.Data4 == [8]byte{} { 179 t.Fatal("Got an all zero GUID, which is overwhelmingly unlikely") 180 } 181 want := fmt.Sprintf("{%08X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:]) 182 got := guid.String() 183 if got != want { 184 t.Fatalf("String = %q; want %q", got, want) 185 } 186 guid2, err := windows.GUIDFromString(got) 187 if err != nil { 188 t.Fatal(err) 189 } 190 if guid2 != guid { 191 t.Fatalf("Did not parse string back to original GUID = %q; want %q", guid2, guid) 192 } 193 _, err = windows.GUIDFromString("not-a-real-guid") 194 if err != syscall.Errno(windows.CO_E_CLASSSTRING) { 195 t.Fatalf("Bad GUID string error = %v; want CO_E_CLASSSTRING", err) 196 } 197 } 198 199 func TestKnownFolderPath(t *testing.T) { 200 token, err := windows.OpenCurrentProcessToken() 201 if err != nil { 202 t.Fatal(err) 203 } 204 defer token.Close() 205 profileDir, err := token.GetUserProfileDirectory() 206 if err != nil { 207 t.Fatal(err) 208 } 209 want := filepath.Join(profileDir, "Desktop") 210 got, err := windows.KnownFolderPath(windows.FOLDERID_Desktop, windows.KF_FLAG_DEFAULT) 211 if err != nil { 212 t.Fatal(err) 213 } 214 if want != got { 215 t.Fatalf("Path = %q; want %q", got, want) 216 } 217 } 218 219 func TestRtlGetVersion(t *testing.T) { 220 version := windows.RtlGetVersion() 221 major, minor, build := windows.RtlGetNtVersionNumbers() 222 // Go is not explicitly added to the application compatibility database, so 223 // these two functions should return the same thing. 224 if version.MajorVersion != major || version.MinorVersion != minor || version.BuildNumber != build { 225 t.Fatalf("%d.%d.%d != %d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber, major, minor, build) 226 } 227 } 228 229 func TestGetNamedSecurityInfo(t *testing.T) { 230 path, err := windows.GetSystemDirectory() 231 if err != nil { 232 t.Fatal(err) 233 } 234 sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION) 235 if err != nil { 236 t.Fatal(err) 237 } 238 if !sd.IsValid() { 239 t.Fatal("Invalid security descriptor") 240 } 241 sdOwner, _, err := sd.Owner() 242 if err != nil { 243 t.Fatal(err) 244 } 245 if !sdOwner.IsValid() { 246 t.Fatal("Invalid security descriptor owner") 247 } 248 } 249 250 func TestGetSecurityInfo(t *testing.T) { 251 sd, err := windows.GetSecurityInfo(windows.CurrentProcess(), windows.SE_KERNEL_OBJECT, windows.DACL_SECURITY_INFORMATION) 252 if err != nil { 253 t.Fatal(err) 254 } 255 if !sd.IsValid() { 256 t.Fatal("Invalid security descriptor") 257 } 258 sdStr := sd.String() 259 if !strings.HasPrefix(sdStr, "D:(A;") { 260 t.Fatalf("DACL = %q; want D:(A;...", sdStr) 261 } 262 } 263 264 func TestSddlConversion(t *testing.T) { 265 sd, err := windows.SecurityDescriptorFromString("O:BA") 266 if err != nil { 267 t.Fatal(err) 268 } 269 if !sd.IsValid() { 270 t.Fatal("Invalid security descriptor") 271 } 272 sdOwner, _, err := sd.Owner() 273 if err != nil { 274 t.Fatal(err) 275 } 276 if !sdOwner.IsValid() { 277 t.Fatal("Invalid security descriptor owner") 278 } 279 if !sdOwner.IsWellKnown(windows.WinBuiltinAdministratorsSid) { 280 t.Fatalf("Owner = %q; want S-1-5-32-544", sdOwner) 281 } 282 } 283 284 func TestBuildSecurityDescriptor(t *testing.T) { 285 const want = "O:SYD:(A;;GA;;;BA)" 286 287 adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid) 288 if err != nil { 289 t.Fatal(err) 290 } 291 systemSid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid) 292 if err != nil { 293 t.Fatal(err) 294 } 295 296 access := []windows.EXPLICIT_ACCESS{{ 297 AccessPermissions: windows.GENERIC_ALL, 298 AccessMode: windows.GRANT_ACCESS, 299 Trustee: windows.TRUSTEE{ 300 TrusteeForm: windows.TRUSTEE_IS_SID, 301 TrusteeType: windows.TRUSTEE_IS_GROUP, 302 TrusteeValue: windows.TrusteeValueFromSID(adminSid), 303 }, 304 }} 305 owner := &windows.TRUSTEE{ 306 TrusteeForm: windows.TRUSTEE_IS_SID, 307 TrusteeType: windows.TRUSTEE_IS_USER, 308 TrusteeValue: windows.TrusteeValueFromSID(systemSid), 309 } 310 311 sd, err := windows.BuildSecurityDescriptor(owner, nil, access, nil, nil) 312 if err != nil { 313 t.Fatal(err) 314 } 315 sd, err = sd.ToAbsolute() 316 if err != nil { 317 t.Fatal(err) 318 } 319 err = sd.SetSACL(nil, false, false) 320 if err != nil { 321 t.Fatal(err) 322 } 323 if got := sd.String(); got != want { 324 t.Fatalf("SD = %q; want %q", got, want) 325 } 326 sd, err = sd.ToSelfRelative() 327 if err != nil { 328 t.Fatal(err) 329 } 330 if got := sd.String(); got != want { 331 t.Fatalf("SD = %q; want %q", got, want) 332 } 333 334 sd, err = windows.NewSecurityDescriptor() 335 if err != nil { 336 t.Fatal(err) 337 } 338 acl, err := windows.ACLFromEntries(access, nil) 339 if err != nil { 340 t.Fatal(err) 341 } 342 err = sd.SetDACL(acl, true, false) 343 if err != nil { 344 t.Fatal(err) 345 } 346 err = sd.SetOwner(systemSid, false) 347 if err != nil { 348 t.Fatal(err) 349 } 350 if got := sd.String(); got != want { 351 t.Fatalf("SD = %q; want %q", got, want) 352 } 353 sd, err = sd.ToSelfRelative() 354 if err != nil { 355 t.Fatal(err) 356 } 357 if got := sd.String(); got != want { 358 t.Fatalf("SD = %q; want %q", got, want) 359 } 360 } 361 362 func TestGetDiskFreeSpaceEx(t *testing.T) { 363 cwd, err := windows.UTF16PtrFromString(".") 364 if err != nil { 365 t.Fatalf(`failed to call UTF16PtrFromString("."): %v`, err) 366 } 367 var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64 368 if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil { 369 t.Fatalf("failed to call GetDiskFreeSpaceEx: %v", err) 370 } 371 372 if freeBytesAvailableToCaller == 0 { 373 t.Errorf("freeBytesAvailableToCaller: got 0; want > 0") 374 } 375 if totalNumberOfBytes == 0 { 376 t.Errorf("totalNumberOfBytes: got 0; want > 0") 377 } 378 if totalNumberOfFreeBytes == 0 { 379 t.Errorf("totalNumberOfFreeBytes: got 0; want > 0") 380 } 381 } 382 383 func TestGetPreferredUILanguages(t *testing.T) { 384 tab := map[string]func(flags uint32) ([]string, error){ 385 "GetProcessPreferredUILanguages": windows.GetProcessPreferredUILanguages, 386 "GetThreadPreferredUILanguages": windows.GetThreadPreferredUILanguages, 387 "GetUserPreferredUILanguages": windows.GetUserPreferredUILanguages, 388 "GetSystemPreferredUILanguages": windows.GetSystemPreferredUILanguages, 389 } 390 for fName, f := range tab { 391 lang, err := f(windows.MUI_LANGUAGE_ID) 392 if err != nil { 393 t.Errorf(`failed to call %v(MUI_LANGUAGE_ID): %v`, fName, err) 394 } 395 for _, l := range lang { 396 _, err := strconv.ParseUint(l, 16, 16) 397 if err != nil { 398 t.Errorf(`%v(MUI_LANGUAGE_ID) returned unexpected LANGID: %v`, fName, l) 399 } 400 } 401 402 lang, err = f(windows.MUI_LANGUAGE_NAME) 403 if err != nil { 404 t.Errorf(`failed to call %v(MUI_LANGUAGE_NAME): %v`, fName, err) 405 } 406 } 407 } 408 409 func TestProcessWorkingSetSizeEx(t *testing.T) { 410 // Grab a handle to the current process 411 hProcess := windows.CurrentProcess() 412 413 // Allocate memory to store the result of the query 414 var minimumWorkingSetSize, maximumWorkingSetSize uintptr 415 416 // Make the system-call 417 var flag uint32 418 windows.GetProcessWorkingSetSizeEx(hProcess, &minimumWorkingSetSize, &maximumWorkingSetSize, &flag) 419 420 // Set the new limits to the current ones 421 if err := windows.SetProcessWorkingSetSizeEx(hProcess, minimumWorkingSetSize, maximumWorkingSetSize, flag); err != nil { 422 t.Error(err) 423 } 424 } 425 426 func TestJobObjectInfo(t *testing.T) { 427 jo, err := windows.CreateJobObject(nil, nil) 428 if err != nil { 429 t.Fatalf("CreateJobObject failed: %v", err) 430 } 431 defer windows.CloseHandle(jo) 432 433 var info windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION 434 435 err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation, 436 uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil) 437 if err != nil { 438 t.Fatalf("QueryInformationJobObject failed: %v", err) 439 } 440 441 const wantMemLimit = 4 * 1024 442 443 info.BasicLimitInformation.LimitFlags |= windows.JOB_OBJECT_LIMIT_PROCESS_MEMORY 444 info.ProcessMemoryLimit = wantMemLimit 445 _, err = windows.SetInformationJobObject(jo, windows.JobObjectExtendedLimitInformation, 446 uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info))) 447 if err != nil { 448 t.Fatalf("SetInformationJobObject failed: %v", err) 449 } 450 451 err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation, 452 uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil) 453 if err != nil { 454 t.Fatalf("QueryInformationJobObject failed: %v", err) 455 } 456 457 if have := info.ProcessMemoryLimit; wantMemLimit != have { 458 t.Errorf("ProcessMemoryLimit is wrong: want %v have %v", wantMemLimit, have) 459 } 460 } 461 462 func TestIsWow64Process2(t *testing.T) { 463 var processMachine, nativeMachine uint16 464 err := windows.IsWow64Process2(windows.CurrentProcess(), &processMachine, &nativeMachine) 465 if errors.Is(err, windows.ERROR_PROC_NOT_FOUND) { 466 maj, min, build := windows.RtlGetNtVersionNumbers() 467 if maj < 10 || (maj == 10 && min == 0 && build < 17763) { 468 t.Skip("not available on older versions of Windows") 469 return 470 } 471 } 472 if err != nil { 473 t.Fatalf("IsWow64Process2 failed: %v", err) 474 } 475 if processMachine == pe.IMAGE_FILE_MACHINE_UNKNOWN { 476 processMachine = nativeMachine 477 } 478 switch { 479 case processMachine == pe.IMAGE_FILE_MACHINE_AMD64 && runtime.GOARCH == "amd64": 480 case processMachine == pe.IMAGE_FILE_MACHINE_I386 && runtime.GOARCH == "386": 481 case processMachine == pe.IMAGE_FILE_MACHINE_ARMNT && runtime.GOARCH == "arm": 482 case processMachine == pe.IMAGE_FILE_MACHINE_ARM64 && runtime.GOARCH == "arm64": 483 default: 484 t.Errorf("IsWow64Process2 is wrong: want %v have %v", runtime.GOARCH, processMachine) 485 } 486 } 487 488 func TestNTStatusString(t *testing.T) { 489 want := "The name limit for the local computer network adapter card was exceeded." 490 got := windows.STATUS_TOO_MANY_NAMES.Error() 491 if want != got { 492 t.Errorf("NTStatus.Error did not return an expected error string - want %q; got %q", want, got) 493 } 494 } 495 496 func TestNTStatusConversion(t *testing.T) { 497 want := windows.ERROR_TOO_MANY_NAMES 498 got := windows.STATUS_TOO_MANY_NAMES.Errno() 499 if want != got { 500 t.Errorf("NTStatus.Errno = %q (0x%x); want %q (0x%x)", got.Error(), got, want.Error(), want) 501 } 502 } 503 504 func TestPEBFilePath(t *testing.T) { 505 peb := windows.RtlGetCurrentPeb() 506 if peb == nil || peb.Ldr == nil { 507 t.Error("unable to retrieve PEB with valid Ldr") 508 } 509 var entry *windows.LDR_DATA_TABLE_ENTRY 510 for cur := peb.Ldr.InMemoryOrderModuleList.Flink; cur != &peb.Ldr.InMemoryOrderModuleList; cur = cur.Flink { 511 e := (*windows.LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(uintptr(unsafe.Pointer(cur)) - unsafe.Offsetof(windows.LDR_DATA_TABLE_ENTRY{}.InMemoryOrderLinks))) 512 if e.DllBase == peb.ImageBaseAddress { 513 entry = e 514 break 515 } 516 } 517 if entry == nil { 518 t.Error("unable to find Ldr entry for current process") 519 } 520 osPath, err := os.Executable() 521 if err != nil { 522 t.Errorf("unable to get path to current executable: %v", err) 523 } 524 pebPath := entry.FullDllName.String() 525 if osPath != pebPath { 526 t.Errorf("peb.Ldr.{entry}.FullDllName = %#q; want %#q", pebPath, osPath) 527 } 528 paramPath := peb.ProcessParameters.ImagePathName.String() 529 if osPath != paramPath { 530 t.Errorf("peb.ProcessParameters.ImagePathName.{entry}.ImagePathName = %#q; want %#q", paramPath, osPath) 531 } 532 osCwd, err := os.Getwd() 533 if err != nil { 534 t.Errorf("unable to get working directory: %v", err) 535 } 536 osCwd = filepath.Clean(osCwd) 537 paramCwd := filepath.Clean(peb.ProcessParameters.CurrentDirectory.DosPath.String()) 538 if paramCwd != osCwd { 539 t.Errorf("peb.ProcessParameters.CurrentDirectory.DosPath = %#q; want %#q", paramCwd, osCwd) 540 } 541 } 542 543 func TestResourceExtraction(t *testing.T) { 544 system32, err := windows.GetSystemDirectory() 545 if err != nil { 546 t.Errorf("unable to find system32 directory: %v", err) 547 } 548 cmd, err := windows.LoadLibrary(filepath.Join(system32, "cmd.exe")) 549 if err != nil { 550 t.Errorf("unable to load cmd.exe: %v", err) 551 } 552 defer windows.FreeLibrary(cmd) 553 rsrc, err := windows.FindResource(cmd, windows.CREATEPROCESS_MANIFEST_RESOURCE_ID, windows.RT_MANIFEST) 554 if err != nil { 555 t.Errorf("unable to find cmd.exe manifest resource: %v", err) 556 } 557 manifest, err := windows.LoadResourceData(cmd, rsrc) 558 if err != nil { 559 t.Errorf("unable to load cmd.exe manifest resource data: %v", err) 560 } 561 if !bytes.Contains(manifest, []byte("</assembly>")) { 562 t.Errorf("did not find </assembly> in manifest") 563 } 564 } 565 566 func FuzzComposeCommandLine(f *testing.F) { 567 f.Add(`C:\foo.exe /bar /baz "-bag qux"`) 568 f.Add(`"C:\Program Files\Go\bin\go.exe" env`) 569 f.Add(`C:\"Program Files"\Go\bin\go.exe env`) 570 f.Add(`C:\"Program Files"\Go\bin\go.exe env`) 571 f.Add(`C:\"Pro"gram Files\Go\bin\go.exe env`) 572 f.Add(``) 573 f.Add(` `) 574 f.Add(`W\"0`) 575 f.Add("\"\f") 576 f.Add("\f") 577 f.Add("\x16") 578 f.Add(`"" ` + strings.Repeat("a", 8193)) 579 f.Add(strings.Repeat(`"" `, 8193)) 580 581 f.Add("\x00abcd") 582 f.Add("ab\x00cd") 583 f.Add("abcd\x00") 584 f.Add("\x00abcd\x00") 585 f.Add("\x00ab\x00cd\x00") 586 f.Add("\x00\x00\x00") 587 f.Add("\x16\x00\x16") 588 f.Add(`C:\Program Files\Go\bin\go.exe` + "\x00env") 589 f.Add(`"C:\Program Files\Go\bin\go.exe"` + "\x00env") 590 f.Add(`C:\"Program Files"\Go\bin\go.exe` + "\x00env") 591 f.Add(`C:\"Pro"gram Files\Go\bin\go.exe` + "\x00env") 592 f.Add("\x00" + strings.Repeat("a", 8192)) 593 f.Add("\x00" + strings.Repeat("a", 8193)) 594 f.Add(strings.Repeat("\x00"+strings.Repeat("a", 8192), 4)) 595 596 f.Fuzz(func(t *testing.T, s string) { 597 // DecomposeCommandLine is the “control” for our experiment: 598 // if it returns a particular list of arguments, then we know 599 // it must be possible to create an input string that produces 600 // exactly those arguments. 601 // 602 // However, DecomposeCommandLine returns an error if the string 603 // contains a NUL byte. In that case, we will fall back to 604 // strings.Split, and be a bit more permissive about the results. 605 args, err := windows.DecomposeCommandLine(s) 606 argsFromSplit := false 607 608 if err == nil { 609 if testing.Verbose() { 610 t.Logf("DecomposeCommandLine(%#q) = %#q", s, args) 611 } 612 } else { 613 t.Logf("DecomposeCommandLine: %v", err) 614 if !strings.Contains(s, "\x00") { 615 // The documentation for CommandLineToArgv takes for granted that 616 // the first argument is a valid file path, and doesn't describe any 617 // specific behavior for malformed arguments. Empirically it seems to 618 // tolerate anything we throw at it, but if we discover cases where it 619 // actually returns an error we might need to relax this check. 620 t.Fatal("(error unexpected)") 621 } 622 623 // Since DecomposeCommandLine can't handle this string, 624 // interpret it as the raw arguments to ComposeCommandLine. 625 args = strings.Split(s, "\x00") 626 argsFromSplit = true 627 for i, arg := range args { 628 if !utf8.ValidString(arg) { 629 // We need to encode the arguments as UTF-16 to pass them to 630 // CommandLineToArgvW, so skip inputs that are not valid: they might 631 // have one or more runes converted to replacement characters. 632 t.Skipf("skipping: input %d is not valid UTF-8", i) 633 } 634 } 635 if testing.Verbose() { 636 t.Logf("using input: %#q", args) 637 } 638 } 639 640 // It's ok if we compose a different command line than what was read. 641 // Just check that we are able to compose something that round-trips 642 // to the same results as the original. 643 commandLine := windows.ComposeCommandLine(args) 644 t.Logf("ComposeCommandLine(_) = %#q", commandLine) 645 646 got, err := windows.DecomposeCommandLine(commandLine) 647 if err != nil { 648 t.Fatalf("DecomposeCommandLine: unexpected error: %v", err) 649 } 650 if testing.Verbose() { 651 t.Logf("DecomposeCommandLine(_) = %#q", got) 652 } 653 654 var badMatches []int 655 for i := range args { 656 if i >= len(got) { 657 badMatches = append(badMatches, i) 658 continue 659 } 660 want := args[i] 661 if got[i] != want { 662 if i == 0 && argsFromSplit { 663 // It is possible that args[0] cannot be encoded exactly, because 664 // CommandLineToArgvW doesn't unescape that argument in the same way 665 // as the others: since the first argument is assumed to be the name 666 // of the program itself, we only have the option of quoted or not. 667 // 668 // If args[0] contains a space or control character, we must quote it 669 // to avoid it being split into multiple arguments. 670 // If args[0] already starts with a quote character, we have no way 671 // to indicate that that character is part of the literal argument. 672 // In either case, if the string already contains a quote character 673 // we must avoid misinterpriting that character as the end of the 674 // quoted argument string. 675 // 676 // Unfortunately, ComposeCommandLine does not return an error, so we 677 // can't report existing quote characters as errors. 678 // Instead, we strip out the problematic quote characters from the 679 // argument, and quote the remainder. 680 // For paths like C:\"Program Files"\Go\bin\go.exe that is arguably 681 // what the caller intended anyway, and for other strings it seems 682 // less harmful than corrupting the subsequent arguments. 683 if got[i] == strings.ReplaceAll(want, `"`, ``) { 684 continue 685 } 686 } 687 badMatches = append(badMatches, i) 688 } 689 } 690 if len(badMatches) != 0 { 691 t.Errorf("Incorrect decomposition at indices: %v", badMatches) 692 } 693 }) 694 } 695 696 func TestWinVerifyTrust(t *testing.T) { 697 evsignedfile := `.\testdata\ev-signed-file.exe` 698 evsignedfile16, err := windows.UTF16PtrFromString(evsignedfile) 699 if err != nil { 700 t.Fatalf("unable to get utf16 of %s: %v", evsignedfile, err) 701 } 702 data := &windows.WinTrustData{ 703 Size: uint32(unsafe.Sizeof(windows.WinTrustData{})), 704 UIChoice: windows.WTD_UI_NONE, 705 RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity. 706 UnionChoice: windows.WTD_CHOICE_FILE, 707 StateAction: windows.WTD_STATEACTION_VERIFY, 708 FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{ 709 Size: uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})), 710 FilePath: evsignedfile16, 711 }), 712 } 713 verifyErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) 714 data.StateAction = windows.WTD_STATEACTION_CLOSE 715 closeErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) 716 if verifyErr != nil { 717 t.Errorf("%s did not verify: %v", evsignedfile, verifyErr) 718 } 719 if closeErr != nil { 720 t.Errorf("unable to free verification resources: %v", closeErr) 721 } 722 723 // Now that we've verified the legitimate file verifies, let's corrupt it and see if it correctly fails. 724 725 corruptedEvsignedfile := filepath.Join(t.TempDir(), "corrupted-file") 726 evsignedfileBytes, err := os.ReadFile(evsignedfile) 727 if err != nil { 728 t.Fatalf("unable to read %s bytes: %v", evsignedfile, err) 729 } 730 if len(evsignedfileBytes) > 0 { 731 evsignedfileBytes[len(evsignedfileBytes)/2-1]++ 732 } 733 err = os.WriteFile(corruptedEvsignedfile, evsignedfileBytes, 0755) 734 if err != nil { 735 t.Fatalf("unable to write corrupted ntoskrnl.exe bytes: %v", err) 736 } 737 evsignedfile16, err = windows.UTF16PtrFromString(corruptedEvsignedfile) 738 if err != nil { 739 t.Fatalf("unable to get utf16 of ntoskrnl.exe: %v", err) 740 } 741 data = &windows.WinTrustData{ 742 Size: uint32(unsafe.Sizeof(windows.WinTrustData{})), 743 UIChoice: windows.WTD_UI_NONE, 744 RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity. 745 UnionChoice: windows.WTD_CHOICE_FILE, 746 StateAction: windows.WTD_STATEACTION_VERIFY, 747 FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{ 748 Size: uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})), 749 FilePath: evsignedfile16, 750 }), 751 } 752 verifyErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) 753 data.StateAction = windows.WTD_STATEACTION_CLOSE 754 closeErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) 755 if verifyErr != windows.Errno(windows.TRUST_E_BAD_DIGEST) { 756 t.Errorf("%s did not fail to verify as expected: %v", corruptedEvsignedfile, verifyErr) 757 } 758 if closeErr != nil { 759 t.Errorf("unable to free verification resources: %v", closeErr) 760 } 761 762 } 763 764 func TestEnumProcesses(t *testing.T) { 765 var ( 766 pids [2]uint32 767 outSize uint32 768 ) 769 err := windows.EnumProcesses(pids[:], &outSize) 770 if err != nil { 771 t.Fatalf("unable to enumerate processes: %v", err) 772 } 773 774 // Regression check for go.dev/issue/60223 775 if outSize != 8 { 776 t.Errorf("unexpected bytes returned: %d", outSize) 777 } 778 // Most likely, this should be [0, 4]. 779 // 0 is the system idle pseudo-process. 4 is the initial system process ID. 780 // This test expects that at least one of the PIDs is not 0. 781 if pids[0] == 0 && pids[1] == 0 { 782 t.Errorf("all PIDs are 0") 783 } 784 } 785 786 func TestProcessModules(t *testing.T) { 787 process, err := windows.GetCurrentProcess() 788 if err != nil { 789 t.Fatalf("unable to get current process: %v", err) 790 } 791 // NB: Assume that we're always the first module. This technically isn't documented anywhere (that I could find), but seems to always hold. 792 var module windows.Handle 793 var cbNeeded uint32 794 err = windows.EnumProcessModules(process, &module, uint32(unsafe.Sizeof(module)), &cbNeeded) 795 if err != nil { 796 t.Fatalf("EnumProcessModules failed: %v", err) 797 } 798 799 var moduleEx windows.Handle 800 err = windows.EnumProcessModulesEx(process, &moduleEx, uint32(unsafe.Sizeof(moduleEx)), &cbNeeded, windows.LIST_MODULES_DEFAULT) 801 if err != nil { 802 t.Fatalf("EnumProcessModulesEx failed: %v", err) 803 } 804 if module != moduleEx { 805 t.Fatalf("module from EnumProcessModules does not match EnumProcessModulesEx: %v != %v", module, moduleEx) 806 } 807 808 exePath, err := os.Executable() 809 if err != nil { 810 t.Fatalf("unable to get current executable path: %v", err) 811 } 812 813 modulePathUTF16 := make([]uint16, len(exePath)+1) 814 err = windows.GetModuleFileNameEx(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16))) 815 if err != nil { 816 t.Fatalf("GetModuleFileNameEx failed: %v", err) 817 } 818 819 modulePath := windows.UTF16ToString(modulePathUTF16) 820 if modulePath != exePath { 821 t.Fatalf("module does not match executable for GetModuleFileNameEx: %s != %s", modulePath, exePath) 822 } 823 824 err = windows.GetModuleBaseName(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16))) 825 if err != nil { 826 t.Fatalf("GetModuleBaseName failed: %v", err) 827 } 828 829 modulePath = windows.UTF16ToString(modulePathUTF16) 830 baseExePath := filepath.Base(exePath) 831 if modulePath != baseExePath { 832 t.Fatalf("module does not match executable for GetModuleBaseName: %s != %s", modulePath, baseExePath) 833 } 834 835 var moduleInfo windows.ModuleInfo 836 err = windows.GetModuleInformation(process, module, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo))) 837 if err != nil { 838 t.Fatalf("GetModuleInformation failed: %v", err) 839 } 840 841 peFile, err := pe.Open(exePath) 842 if err != nil { 843 t.Fatalf("unable to open current executable: %v", err) 844 } 845 defer peFile.Close() 846 847 var peSizeOfImage uint32 848 switch runtime.GOARCH { 849 case "amd64", "arm64": 850 peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader64).SizeOfImage 851 case "386", "arm": 852 peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader32).SizeOfImage 853 default: 854 t.Fatalf("unable to test GetModuleInformation on arch %v", runtime.GOARCH) 855 } 856 857 if moduleInfo.SizeOfImage != peSizeOfImage { 858 t.Fatalf("module size does not match executable: %v != %v", moduleInfo.SizeOfImage, peSizeOfImage) 859 } 860 } 861 862 func TestQueryWorkingSetEx(t *testing.T) { 863 var a int 864 865 process := windows.CurrentProcess() 866 information := windows.PSAPI_WORKING_SET_EX_INFORMATION{ 867 VirtualAddress: windows.Pointer(unsafe.Pointer(&a)), 868 } 869 infos := []windows.PSAPI_WORKING_SET_EX_INFORMATION{information} 870 871 cb := uint32(uintptr(len(infos)) * unsafe.Sizeof(infos[0])) 872 if err := windows.QueryWorkingSetEx(process, uintptr(unsafe.Pointer(&infos[0])), cb); err != nil { 873 t.Fatalf("%+v", err) 874 } 875 876 if !infos[0].VirtualAttributes.Valid() { 877 t.Errorf("memory location not valid") 878 } 879 } 880 881 func TestReadWriteProcessMemory(t *testing.T) { 882 testBuffer := []byte{0xBA, 0xAD, 0xF0, 0x0D} 883 884 process, err := windows.GetCurrentProcess() 885 if err != nil { 886 t.Fatalf("unable to get current process: %v", err) 887 } 888 889 buffer := make([]byte, len(testBuffer)) 890 err = windows.ReadProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil) 891 if err != nil { 892 t.Errorf("ReadProcessMemory failed: %v", err) 893 } 894 if !bytes.Equal(testBuffer, buffer) { 895 t.Errorf("bytes read does not match buffer: 0x%X != 0x%X", testBuffer, buffer) 896 } 897 898 buffer = []byte{0xDE, 0xAD, 0xBE, 0xEF} 899 err = windows.WriteProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil) 900 if err != nil { 901 t.Errorf("WriteProcessMemory failed: %v", err) 902 } 903 if !bytes.Equal(testBuffer, buffer) { 904 t.Errorf("bytes written does not match buffer: 0x%X != 0x%X", testBuffer, buffer) 905 } 906 } 907 908 func TestSystemModuleVersions(t *testing.T) { 909 var modules []windows.RTL_PROCESS_MODULE_INFORMATION 910 for bufferSize := uint32(128 * 1024); ; { 911 moduleBuffer := make([]byte, bufferSize) 912 err := windows.NtQuerySystemInformation(windows.SystemModuleInformation, unsafe.Pointer(&moduleBuffer[0]), bufferSize, &bufferSize) 913 switch err { 914 case windows.STATUS_INFO_LENGTH_MISMATCH: 915 continue 916 case nil: 917 break 918 default: 919 t.Error(err) 920 return 921 } 922 mods := (*windows.RTL_PROCESS_MODULES)(unsafe.Pointer(&moduleBuffer[0])) 923 modules = unsafe.Slice(&mods.Modules[0], int(mods.NumberOfModules)) 924 break 925 } 926 for i := range modules { 927 moduleName := windows.ByteSliceToString(modules[i].FullPathName[modules[i].OffsetToFileName:]) 928 driverPath := `\\?\GLOBALROOT` + windows.ByteSliceToString(modules[i].FullPathName[:]) 929 var zero windows.Handle 930 infoSize, err := windows.GetFileVersionInfoSize(driverPath, &zero) 931 if err != nil { 932 if err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_RESOURCE_TYPE_NOT_FOUND { 933 t.Errorf("%v: %v", moduleName, err) 934 } 935 continue 936 } 937 versionInfo := make([]byte, infoSize) 938 if err = windows.GetFileVersionInfo(driverPath, 0, infoSize, unsafe.Pointer(&versionInfo[0])); err != nil { 939 t.Errorf("%v: %v", moduleName, err) 940 continue 941 } 942 var fixedInfo *windows.VS_FIXEDFILEINFO 943 fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo)) 944 err = windows.VerQueryValue(unsafe.Pointer(&versionInfo[0]), `\`, (unsafe.Pointer)(&fixedInfo), &fixedInfoLen) 945 if err != nil { 946 t.Errorf("%v: %v", moduleName, err) 947 continue 948 } 949 t.Logf("%s: v%d.%d.%d.%d", moduleName, 950 (fixedInfo.FileVersionMS>>16)&0xff, 951 (fixedInfo.FileVersionMS>>0)&0xff, 952 (fixedInfo.FileVersionLS>>16)&0xff, 953 (fixedInfo.FileVersionLS>>0)&0xff) 954 } 955 } 956 957 type fileRenameInformation struct { 958 ReplaceIfExists uint32 959 RootDirectory windows.Handle 960 FileNameLength uint32 961 FileName [1]uint16 962 } 963 964 func TestNtCreateFileAndNtSetInformationFile(t *testing.T) { 965 var iosb windows.IO_STATUS_BLOCK 966 var allocSize int64 = 0 967 // Open test directory with NtCreateFile. 968 testDirPath := t.TempDir() 969 objectName, err := windows.NewNTUnicodeString("\\??\\" + testDirPath) 970 if err != nil { 971 t.Fatal(err) 972 } 973 oa := &windows.OBJECT_ATTRIBUTES{ 974 ObjectName: objectName, 975 } 976 oa.Length = uint32(unsafe.Sizeof(*oa)) 977 var testDirHandle windows.Handle 978 err = windows.NtCreateFile(&testDirHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE, oa, &iosb, 979 &allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_OPEN, 980 windows.FILE_DIRECTORY_FILE, 0, 0) 981 if err != nil { 982 t.Fatalf("NtCreateFile(%v) failed: %v", testDirPath, err) 983 } 984 defer windows.CloseHandle(testDirHandle) 985 // Create a file in test directory with NtCreateFile. 986 fileName := "filename" 987 filePath := filepath.Join(testDirPath, fileName) 988 objectName, err = windows.NewNTUnicodeString(fileName) 989 if err != nil { 990 t.Fatal(err) 991 } 992 oa.RootDirectory = testDirHandle 993 oa.ObjectName = objectName 994 var fileHandle windows.Handle 995 err = windows.NtCreateFile(&fileHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE, oa, &iosb, 996 &allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_CREATE, 997 0, 0, 0) 998 if err != nil { 999 t.Fatalf("NtCreateFile(%v) failed: %v", filePath, err) 1000 } 1001 defer windows.CloseHandle(fileHandle) 1002 _, err = os.Stat(filePath) 1003 if err != nil { 1004 t.Fatalf("cannot stat file created with NtCreatefile: %v", err) 1005 } 1006 // Rename file with NtSetInformationFile. 1007 newName := "newname" 1008 newPath := filepath.Join(testDirPath, newName) 1009 newNameUTF16, err := windows.UTF16FromString(newName) 1010 if err != nil { 1011 t.Fatal(err) 1012 } 1013 fileNameLen := len(newNameUTF16)*2 - 2 1014 var dummyFileRenameInfo fileRenameInformation 1015 bufferSize := int(unsafe.Offsetof(dummyFileRenameInfo.FileName)) + fileNameLen 1016 buffer := make([]byte, bufferSize) 1017 typedBufferPtr := (*fileRenameInformation)(unsafe.Pointer(&buffer[0])) 1018 typedBufferPtr.ReplaceIfExists = windows.FILE_RENAME_REPLACE_IF_EXISTS | windows.FILE_RENAME_POSIX_SEMANTICS 1019 typedBufferPtr.FileNameLength = uint32(fileNameLen) 1020 copy((*[windows.MAX_LONG_PATH]uint16)(unsafe.Pointer(&typedBufferPtr.FileName[0]))[:fileNameLen/2:fileNameLen/2], newNameUTF16) 1021 err = windows.NtSetInformationFile(fileHandle, &iosb, &buffer[0], uint32(bufferSize), windows.FileRenameInformation) 1022 if err != nil { 1023 t.Fatalf("NtSetInformationFile(%v) failed: %v", newPath, err) 1024 } 1025 _, err = os.Stat(newPath) 1026 if err != nil { 1027 t.Fatalf("cannot stat rename target %v: %v", newPath, err) 1028 } 1029 } 1030 1031 var deviceClassNetGUID = &windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}} 1032 var deviceInterfaceNetGUID = &windows.GUID{0xcac88484, 0x7515, 0x4c03, [8]byte{0x82, 0xe6, 0x71, 0xa8, 0x7a, 0xba, 0xc3, 0x61}} 1033 1034 func TestListLoadedNetworkDevices(t *testing.T) { 1035 devInfo, err := windows.SetupDiGetClassDevsEx(deviceClassNetGUID, "", 0, windows.DIGCF_PRESENT, 0, "") 1036 if err != nil { 1037 t.Fatal(err) 1038 } 1039 defer devInfo.Close() 1040 for i := 0; ; i++ { 1041 devInfoData, err := devInfo.EnumDeviceInfo(i) 1042 if err != nil { 1043 if err == windows.ERROR_NO_MORE_ITEMS { 1044 break 1045 } 1046 continue 1047 } 1048 friendlyName, err := devInfo.DeviceRegistryProperty(devInfoData, windows.SPDRP_DEVICEDESC) 1049 if err != nil { 1050 t.Fatal(err) 1051 } 1052 var status, problemCode uint32 1053 err = windows.CM_Get_DevNode_Status(&status, &problemCode, devInfoData.DevInst, 0) 1054 if err != nil || (status&windows.DN_DRIVER_LOADED|windows.DN_STARTED) != windows.DN_DRIVER_LOADED|windows.DN_STARTED { 1055 continue 1056 } 1057 instanceId, err := devInfo.DeviceInstanceID(devInfoData) 1058 if err != nil { 1059 t.Fatal(err) 1060 } 1061 interfaces, err := windows.CM_Get_Device_Interface_List(instanceId, deviceInterfaceNetGUID, windows.CM_GET_DEVICE_INTERFACE_LIST_PRESENT) 1062 if err != nil || len(interfaces) == 0 { 1063 continue 1064 } 1065 t.Logf("%s - %s", friendlyName, interfaces[0]) 1066 } 1067 } 1068 1069 func TestListWireGuardDrivers(t *testing.T) { 1070 devInfo, err := windows.SetupDiCreateDeviceInfoListEx(deviceClassNetGUID, 0, "") 1071 if err != nil { 1072 t.Fatal(err) 1073 } 1074 defer devInfo.Close() 1075 devInfoData, err := devInfo.CreateDeviceInfo("WireGuard", deviceClassNetGUID, "", 0, windows.DICD_GENERATE_ID) 1076 if err != nil { 1077 t.Fatal(err) 1078 } 1079 err = devInfo.SetDeviceRegistryProperty(devInfoData, windows.SPDRP_HARDWAREID, []byte("W\x00i\x00r\x00e\x00G\x00u\x00a\x00r\x00d\x00\x00\x00\x00\x00")) 1080 if err != nil { 1081 t.Fatal(err) 1082 } 1083 err = devInfo.BuildDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER) 1084 if err != nil { 1085 t.Fatal(err) 1086 } 1087 defer devInfo.DestroyDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER) 1088 for i := 0; ; i++ { 1089 drvInfoData, err := devInfo.EnumDriverInfo(devInfoData, windows.SPDIT_COMPATDRIVER, i) 1090 if err != nil { 1091 if err == windows.ERROR_NO_MORE_ITEMS { 1092 break 1093 } 1094 continue 1095 } 1096 drvInfoDetailData, err := devInfo.DriverInfoDetail(devInfoData, drvInfoData) 1097 if err != nil { 1098 t.Error(err) 1099 continue 1100 } 1101 t.Logf("%s - %s", drvInfoData.Description(), drvInfoDetailData.InfFileName()) 1102 } 1103 } 1104 1105 func TestProcThreadAttributeHandleList(t *testing.T) { 1106 const sentinel = "the gopher dance" 1107 system32, err := windows.GetSystemDirectory() 1108 if err != nil { 1109 t.Fatal(err) 1110 } 1111 executable16, err := windows.UTF16PtrFromString(filepath.Join(system32, "cmd.exe")) 1112 if err != nil { 1113 t.Fatal(err) 1114 } 1115 args16, err := windows.UTF16PtrFromString(windows.ComposeCommandLine([]string{"/c", "echo " + sentinel})) 1116 if err != nil { 1117 t.Fatal(err) 1118 } 1119 attributeList, err := windows.NewProcThreadAttributeList(1) 1120 if err != nil { 1121 t.Fatal(err) 1122 } 1123 defer attributeList.Delete() 1124 si := &windows.StartupInfoEx{ 1125 StartupInfo: windows.StartupInfo{Cb: uint32(unsafe.Sizeof(windows.StartupInfoEx{}))}, 1126 ProcThreadAttributeList: attributeList.List(), 1127 } 1128 pipeR, pipeW, err := os.Pipe() 1129 if err != nil { 1130 t.Fatal(err) 1131 } 1132 defer pipeR.Close() 1133 defer pipeW.Close() 1134 func() { 1135 // We allocate handles in a closure to provoke a UaF in the case of attributeList.Update being buggy. 1136 handles := []windows.Handle{windows.Handle(pipeW.Fd())} 1137 attributeList.Update(windows.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&handles[0]), uintptr(len(handles))*unsafe.Sizeof(handles[0])) 1138 si.Flags |= windows.STARTF_USESTDHANDLES 1139 si.StdOutput = handles[0] 1140 // Go 1.16's pipe handles aren't inheritable, so mark it explicitly as such here. 1141 windows.SetHandleInformation(handles[0], windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT) 1142 }() 1143 pi := new(windows.ProcessInformation) 1144 err = windows.CreateProcess(executable16, args16, nil, nil, true, windows.CREATE_DEFAULT_ERROR_MODE|windows.CREATE_UNICODE_ENVIRONMENT|windows.EXTENDED_STARTUPINFO_PRESENT, nil, nil, &si.StartupInfo, pi) 1145 if err != nil { 1146 t.Fatal(err) 1147 } 1148 defer windows.CloseHandle(pi.Thread) 1149 defer windows.CloseHandle(pi.Process) 1150 pipeR.SetReadDeadline(time.Now().Add(time.Minute)) 1151 out, _, err := bufio.NewReader(pipeR).ReadLine() 1152 if err != nil { 1153 t.Fatal(err) 1154 } 1155 if string(out) != sentinel { 1156 t.Fatalf("got %q; want %q", out, sentinel) 1157 } 1158 } 1159 1160 func TestWSALookupService(t *testing.T) { 1161 var flags uint32 = windows.LUP_CONTAINERS 1162 flags |= windows.LUP_RETURN_NAME 1163 flags |= windows.LUP_RETURN_ADDR 1164 1165 var querySet windows.WSAQUERYSET 1166 querySet.NameSpace = windows.NS_BTH 1167 querySet.Size = uint32(unsafe.Sizeof(windows.WSAQUERYSET{})) 1168 1169 var handle windows.Handle 1170 err := windows.WSALookupServiceBegin(&querySet, flags, &handle) 1171 if err != nil { 1172 if errors.Is(err, windows.WSASERVICE_NOT_FOUND) { 1173 t.Skip("WSA Service not found, so skip this test") 1174 } 1175 t.Fatal(err) 1176 } 1177 1178 defer windows.WSALookupServiceEnd(handle) 1179 1180 n := int32(unsafe.Sizeof(windows.WSAQUERYSET{})) 1181 buf := make([]byte, n) 1182 items_loop: 1183 for { 1184 q := (*windows.WSAQUERYSET)(unsafe.Pointer(&buf[0])) 1185 err := windows.WSALookupServiceNext(handle, flags, &n, q) 1186 switch err { 1187 case windows.WSA_E_NO_MORE, windows.WSAENOMORE: 1188 // no more data available - break the loop 1189 break items_loop 1190 case windows.WSAEFAULT: 1191 // buffer is too small - reallocate and try again 1192 buf = make([]byte, n) 1193 case nil: 1194 // found a record - display the item and fetch next item 1195 var addr string 1196 for _, e := range q.SaBuffer.RemoteAddr.Sockaddr.Addr.Data { 1197 if e != 0 { 1198 addr += fmt.Sprintf("%x", e) 1199 } 1200 } 1201 t.Logf("%s -> %s\n", windows.UTF16PtrToString(q.ServiceInstanceName), addr) 1202 1203 default: 1204 t.Fatal(err) 1205 } 1206 } 1207 } 1208 1209 func TestTimePeriod(t *testing.T) { 1210 if err := windows.TimeBeginPeriod(1); err != nil { 1211 t.Fatal(err) 1212 } 1213 if err := windows.TimeEndPeriod(1); err != nil { 1214 t.Fatal(err) 1215 } 1216 } 1217 1218 func TestGetStartupInfo(t *testing.T) { 1219 var si windows.StartupInfo 1220 err := windows.GetStartupInfo(&si) 1221 if err != nil { 1222 // see https://go.dev/issue/31316 1223 t.Fatalf("GetStartupInfo: got error %v, want nil", err) 1224 } 1225 } 1226 1227 func TestAddRemoveDllDirectory(t *testing.T) { 1228 if _, err := exec.LookPath("gcc"); err != nil { 1229 t.Skip("skipping test: gcc is missing") 1230 } 1231 dllSrc := `#include <stdint.h> 1232 #include <windows.h> 1233 1234 uintptr_t beep(void) { 1235 return 5; 1236 }` 1237 tmpdir := t.TempDir() 1238 srcname := "beep.c" 1239 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(dllSrc), 0) 1240 if err != nil { 1241 t.Fatal(err) 1242 } 1243 name := "beep.dll" 1244 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname) 1245 cmd.Dir = tmpdir 1246 out, err := cmd.CombinedOutput() 1247 if err != nil { 1248 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 1249 } 1250 1251 if _, err := windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS); err == nil { 1252 t.Fatal("LoadLibraryEx unexpectedly found beep.dll") 1253 } 1254 1255 dllCookie, err := windows.AddDllDirectory(windows.StringToUTF16Ptr(tmpdir)) 1256 if err != nil { 1257 t.Fatalf("AddDllDirectory failed: %s", err) 1258 } 1259 1260 handle, err := windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS) 1261 if err != nil { 1262 t.Fatalf("LoadLibraryEx failed: %s", err) 1263 } 1264 1265 if err := windows.FreeLibrary(handle); err != nil { 1266 t.Fatalf("FreeLibrary failed: %s", err) 1267 } 1268 1269 if err := windows.RemoveDllDirectory(dllCookie); err != nil { 1270 t.Fatalf("RemoveDllDirectory failed: %s", err) 1271 } 1272 1273 _, err = windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS) 1274 if err == nil { 1275 t.Fatal("LoadLibraryEx unexpectedly found beep.dll") 1276 } 1277 }