Source file src/syscall/syscall_windows_test.go

     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 syscall_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/testenv"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  	"syscall"
    15  	"testing"
    16  )
    17  
    18  func TestOpen(t *testing.T) {
    19  	t.Parallel()
    20  
    21  	dir := t.TempDir()
    22  	file := filepath.Join(dir, "a")
    23  	f, err := os.Create(file)
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  	f.Close()
    28  
    29  	tests := []struct {
    30  		path string
    31  		flag int
    32  		err  error
    33  	}{
    34  		{dir, syscall.O_RDONLY, nil},
    35  		{dir, syscall.O_CREAT, nil},
    36  		{dir, syscall.O_RDONLY | syscall.O_CREAT, nil},
    37  		{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE, nil},
    38  		{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE | os.O_TRUNC, nil},
    39  		{file, syscall.O_WRONLY | syscall.O_RDWR, nil},
    40  		{dir, syscall.O_WRONLY | syscall.O_RDWR, nil},
    41  		{dir, syscall.O_RDONLY | syscall.O_TRUNC, syscall.ERROR_ACCESS_DENIED},
    42  		{dir, syscall.O_WRONLY, syscall.EISDIR},
    43  		{dir, syscall.O_RDWR, syscall.EISDIR},
    44  	}
    45  	for i, tt := range tests {
    46  		h, err := syscall.Open(tt.path, tt.flag, 0o660)
    47  		if err == nil {
    48  			syscall.CloseHandle(h)
    49  		}
    50  		if err != tt.err {
    51  			t.Errorf("%d: Open got %q, want %q", i, err, tt.err)
    52  		}
    53  	}
    54  }
    55  
    56  func TestComputerName(t *testing.T) {
    57  	name, err := syscall.ComputerName()
    58  	if err != nil {
    59  		t.Fatalf("ComputerName failed: %v", err)
    60  	}
    61  	if len(name) == 0 {
    62  		t.Error("ComputerName returned empty string")
    63  	}
    64  }
    65  
    66  func TestWin32finddata(t *testing.T) {
    67  	dir := t.TempDir()
    68  
    69  	path := filepath.Join(dir, "long_name.and_extension")
    70  	f, err := os.Create(path)
    71  	if err != nil {
    72  		t.Fatalf("failed to create %v: %v", path, err)
    73  	}
    74  	f.Close()
    75  
    76  	type X struct {
    77  		fd  syscall.Win32finddata
    78  		got byte
    79  		pad [10]byte // to protect ourselves
    80  
    81  	}
    82  	var want byte = 2 // it is unlikely to have this character in the filename
    83  	x := X{got: want}
    84  
    85  	pathp, _ := syscall.UTF16PtrFromString(path)
    86  	h, err := syscall.FindFirstFile(pathp, &(x.fd))
    87  	if err != nil {
    88  		t.Fatalf("FindFirstFile failed: %v", err)
    89  	}
    90  	err = syscall.FindClose(h)
    91  	if err != nil {
    92  		t.Fatalf("FindClose failed: %v", err)
    93  	}
    94  
    95  	if x.got != want {
    96  		t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
    97  	}
    98  }
    99  
   100  func abort(funcname string, err error) {
   101  	panic(funcname + " failed: " + err.Error())
   102  }
   103  
   104  func ExampleLoadLibrary() {
   105  	h, err := syscall.LoadLibrary("kernel32.dll")
   106  	if err != nil {
   107  		abort("LoadLibrary", err)
   108  	}
   109  	defer syscall.FreeLibrary(h)
   110  	proc, err := syscall.GetProcAddress(h, "GetVersion")
   111  	if err != nil {
   112  		abort("GetProcAddress", err)
   113  	}
   114  	r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
   115  	major := byte(r)
   116  	minor := uint8(r >> 8)
   117  	build := uint16(r >> 16)
   118  	print("windows version ", major, ".", minor, " (Build ", build, ")\n")
   119  }
   120  
   121  func TestTOKEN_ALL_ACCESS(t *testing.T) {
   122  	if syscall.TOKEN_ALL_ACCESS != 0xF01FF {
   123  		t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", syscall.TOKEN_ALL_ACCESS)
   124  	}
   125  }
   126  
   127  func TestStdioAreInheritable(t *testing.T) {
   128  	testenv.MustHaveGoBuild(t)
   129  	testenv.MustHaveCGO(t)
   130  	testenv.MustHaveExecPath(t, "gcc")
   131  
   132  	tmpdir := t.TempDir()
   133  
   134  	// build go dll
   135  	const dlltext = `
   136  package main
   137  
   138  import "C"
   139  import (
   140  	"fmt"
   141  )
   142  
   143  //export HelloWorld
   144  func HelloWorld() {
   145  	fmt.Println("Hello World")
   146  }
   147  
   148  func main() {}
   149  `
   150  	dllsrc := filepath.Join(tmpdir, "helloworld.go")
   151  	err := os.WriteFile(dllsrc, []byte(dlltext), 0644)
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	dll := filepath.Join(tmpdir, "helloworld.dll")
   156  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", dllsrc)
   157  	out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
   158  	if err != nil {
   159  		t.Fatalf("failed to build go library: %s\n%s", err, out)
   160  	}
   161  
   162  	// build c exe
   163  	const exetext = `
   164  #include <stdlib.h>
   165  #include <windows.h>
   166  int main(int argc, char *argv[])
   167  {
   168  	system("hostname");
   169  	((void(*)(void))GetProcAddress(LoadLibraryA(%q), "HelloWorld"))();
   170  	system("hostname");
   171  	return 0;
   172  }
   173  `
   174  	exe := filepath.Join(tmpdir, "helloworld.exe")
   175  	cmd = exec.Command("gcc", "-o", exe, "-xc", "-")
   176  	cmd.Stdin = strings.NewReader(fmt.Sprintf(exetext, dll))
   177  	out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
   178  	if err != nil {
   179  		t.Fatalf("failed to build c executable: %s\n%s", err, out)
   180  	}
   181  	out, err = exec.Command(exe).Output()
   182  	if err != nil {
   183  		t.Fatalf("c program execution failed: %v: %v", err, string(out))
   184  	}
   185  
   186  	hostname, err := os.Hostname()
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	have := strings.ReplaceAll(string(out), "\n", "")
   192  	have = strings.ReplaceAll(have, "\r", "")
   193  	want := fmt.Sprintf("%sHello World%s", hostname, hostname)
   194  	if have != want {
   195  		t.Fatalf("c program output is wrong: got %q, want %q", have, want)
   196  	}
   197  }
   198  
   199  func TestGetwd_DoesNotPanicWhenPathIsLong(t *testing.T) {
   200  	// Regression test for https://github.com/golang/go/issues/60051.
   201  	tmp := t.TempDir()
   202  	t.Chdir(tmp)
   203  
   204  	// The length of a filename is also limited, so we can't reproduce the
   205  	// crash by creating a single directory with a very long name; we need two
   206  	// layers.
   207  	a200 := strings.Repeat("a", 200)
   208  	dirname := filepath.Join(tmp, a200, a200)
   209  
   210  	err := os.MkdirAll(dirname, 0o700)
   211  	if err != nil {
   212  		t.Skipf("MkdirAll failed: %v", err)
   213  	}
   214  	err = os.Chdir(dirname)
   215  	if err != nil {
   216  		t.Skipf("Chdir failed: %v", err)
   217  	}
   218  
   219  	syscall.Getwd()
   220  }
   221  
   222  func TestGetStartupInfo(t *testing.T) {
   223  	var si syscall.StartupInfo
   224  	err := syscall.GetStartupInfo(&si)
   225  	if err != nil {
   226  		// see https://go.dev/issue/31316
   227  		t.Fatalf("GetStartupInfo: got error %v, want nil", err)
   228  	}
   229  }
   230  
   231  func TestSyscallAllocations(t *testing.T) {
   232  	testenv.SkipIfOptimizationOff(t)
   233  
   234  	// Test that syscall.SyscallN arguments do not escape.
   235  	// The function used (in this case GetVersion) doesn't matter
   236  	// as long as it is always available and doesn't panic.
   237  	h, err := syscall.LoadLibrary("kernel32.dll")
   238  	if err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	defer syscall.FreeLibrary(h)
   242  	proc, err := syscall.GetProcAddress(h, "GetVersion")
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  
   247  	testAllocs := func(t *testing.T, name string, fn func() error) {
   248  		t.Run(name, func(t *testing.T) {
   249  			n := int(testing.AllocsPerRun(10, func() {
   250  				if err := fn(); err != nil {
   251  					t.Fatalf("%s: %v", name, err)
   252  				}
   253  			}))
   254  			if n > 0 {
   255  				t.Errorf("allocs = %d, want 0", n)
   256  			}
   257  		})
   258  	}
   259  
   260  	testAllocs(t, "SyscallN", func() error {
   261  		r0, _, e1 := syscall.SyscallN(proc, 0, 0, 0)
   262  		if r0 == 0 {
   263  			return syscall.Errno(e1)
   264  		}
   265  		return nil
   266  	})
   267  	testAllocs(t, "Syscall", func() error {
   268  		r0, _, e1 := syscall.Syscall(proc, 3, 0, 0, 0)
   269  		if r0 == 0 {
   270  			return syscall.Errno(e1)
   271  		}
   272  		return nil
   273  	})
   274  }
   275  
   276  func FuzzUTF16FromString(f *testing.F) {
   277  	f.Add("hi")           // ASCII
   278  	f.Add("รข")            // latin1
   279  	f.Add("ใญใ“")           // plane 0
   280  	f.Add("๐Ÿ˜ƒ")            // extra Plane 0
   281  	f.Add("\x90")         // invalid byte
   282  	f.Add("\xe3\x81")     // truncated
   283  	f.Add("\xe3\xc1\x81") // invalid middle byte
   284  
   285  	f.Fuzz(func(t *testing.T, tst string) {
   286  		res, err := syscall.UTF16FromString(tst)
   287  		if err != nil {
   288  			if strings.Contains(tst, "\x00") {
   289  				t.Skipf("input %q contains a NUL byte", tst)
   290  			}
   291  			t.Fatalf("UTF16FromString(%q): %v", tst, err)
   292  		}
   293  		t.Logf("UTF16FromString(%q) = %04x", tst, res)
   294  
   295  		if len(res) < 1 || res[len(res)-1] != 0 {
   296  			t.Fatalf("missing NUL terminator")
   297  		}
   298  		if len(res) > len(tst)+1 {
   299  			t.Fatalf("len(%04x) > len(%q)+1", res, tst)
   300  		}
   301  	})
   302  }
   303  
   304  func BenchmarkErrnoString(b *testing.B) {
   305  	b.ReportAllocs()
   306  	for b.Loop() {
   307  		_ = syscall.Errno(2).Error()
   308  	}
   309  }
   310  

View as plain text