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
5package syscall_test
6
7import (
8	"fmt"
9	"internal/testenv"
10	"os"
11	"os/exec"
12	"path/filepath"
13	"strings"
14	"syscall"
15	"testing"
16)
17
18func TestOpen_Dir(t *testing.T) {
19	dir := t.TempDir()
20
21	h, err := syscall.Open(dir, syscall.O_RDONLY, 0)
22	if err != nil {
23		t.Fatalf("Open failed: %v", err)
24	}
25	syscall.CloseHandle(h)
26	h, err = syscall.Open(dir, syscall.O_RDONLY|syscall.O_TRUNC, 0)
27	if err == nil {
28		t.Error("Open should have failed")
29	} else {
30		syscall.CloseHandle(h)
31	}
32	h, err = syscall.Open(dir, syscall.O_RDONLY|syscall.O_CREAT, 0)
33	if err == nil {
34		t.Error("Open should have failed")
35	} else {
36		syscall.CloseHandle(h)
37	}
38}
39
40func TestComputerName(t *testing.T) {
41	name, err := syscall.ComputerName()
42	if err != nil {
43		t.Fatalf("ComputerName failed: %v", err)
44	}
45	if len(name) == 0 {
46		t.Error("ComputerName returned empty string")
47	}
48}
49
50func TestWin32finddata(t *testing.T) {
51	dir := t.TempDir()
52
53	path := filepath.Join(dir, "long_name.and_extension")
54	f, err := os.Create(path)
55	if err != nil {
56		t.Fatalf("failed to create %v: %v", path, err)
57	}
58	f.Close()
59
60	type X struct {
61		fd  syscall.Win32finddata
62		got byte
63		pad [10]byte // to protect ourselves
64
65	}
66	var want byte = 2 // it is unlikely to have this character in the filename
67	x := X{got: want}
68
69	pathp, _ := syscall.UTF16PtrFromString(path)
70	h, err := syscall.FindFirstFile(pathp, &(x.fd))
71	if err != nil {
72		t.Fatalf("FindFirstFile failed: %v", err)
73	}
74	err = syscall.FindClose(h)
75	if err != nil {
76		t.Fatalf("FindClose failed: %v", err)
77	}
78
79	if x.got != want {
80		t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
81	}
82}
83
84func abort(funcname string, err error) {
85	panic(funcname + " failed: " + err.Error())
86}
87
88func ExampleLoadLibrary() {
89	h, err := syscall.LoadLibrary("kernel32.dll")
90	if err != nil {
91		abort("LoadLibrary", err)
92	}
93	defer syscall.FreeLibrary(h)
94	proc, err := syscall.GetProcAddress(h, "GetVersion")
95	if err != nil {
96		abort("GetProcAddress", err)
97	}
98	r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
99	major := byte(r)
100	minor := uint8(r >> 8)
101	build := uint16(r >> 16)
102	print("windows version ", major, ".", minor, " (Build ", build, ")\n")
103}
104
105func TestTOKEN_ALL_ACCESS(t *testing.T) {
106	if syscall.TOKEN_ALL_ACCESS != 0xF01FF {
107		t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", syscall.TOKEN_ALL_ACCESS)
108	}
109}
110
111func TestStdioAreInheritable(t *testing.T) {
112	testenv.MustHaveGoBuild(t)
113	testenv.MustHaveCGO(t)
114	testenv.MustHaveExecPath(t, "gcc")
115
116	tmpdir := t.TempDir()
117
118	// build go dll
119	const dlltext = `
120package main
121
122import "C"
123import (
124	"fmt"
125)
126
127//export HelloWorld
128func HelloWorld() {
129	fmt.Println("Hello World")
130}
131
132func main() {}
133`
134	dllsrc := filepath.Join(tmpdir, "helloworld.go")
135	err := os.WriteFile(dllsrc, []byte(dlltext), 0644)
136	if err != nil {
137		t.Fatal(err)
138	}
139	dll := filepath.Join(tmpdir, "helloworld.dll")
140	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", dllsrc)
141	out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
142	if err != nil {
143		t.Fatalf("failed to build go library: %s\n%s", err, out)
144	}
145
146	// build c exe
147	const exetext = `
148#include <stdlib.h>
149#include <windows.h>
150int main(int argc, char *argv[])
151{
152	system("hostname");
153	((void(*)(void))GetProcAddress(LoadLibraryA(%q), "HelloWorld"))();
154	system("hostname");
155	return 0;
156}
157`
158	exe := filepath.Join(tmpdir, "helloworld.exe")
159	cmd = exec.Command("gcc", "-o", exe, "-xc", "-")
160	cmd.Stdin = strings.NewReader(fmt.Sprintf(exetext, dll))
161	out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
162	if err != nil {
163		t.Fatalf("failed to build c executable: %s\n%s", err, out)
164	}
165	out, err = exec.Command(exe).Output()
166	if err != nil {
167		t.Fatalf("c program execution failed: %v: %v", err, string(out))
168	}
169
170	hostname, err := os.Hostname()
171	if err != nil {
172		t.Fatal(err)
173	}
174
175	have := strings.ReplaceAll(string(out), "\n", "")
176	have = strings.ReplaceAll(have, "\r", "")
177	want := fmt.Sprintf("%sHello World%s", hostname, hostname)
178	if have != want {
179		t.Fatalf("c program output is wrong: got %q, want %q", have, want)
180	}
181}
182
183func TestGetwd_DoesNotPanicWhenPathIsLong(t *testing.T) {
184	// Regression test for https://github.com/golang/go/issues/60051.
185
186	// The length of a filename is also limited, so we can't reproduce the
187	// crash by creating a single directory with a very long name; we need two
188	// layers.
189	a200 := strings.Repeat("a", 200)
190	dirname := filepath.Join(t.TempDir(), a200, a200)
191
192	err := os.MkdirAll(dirname, 0o700)
193	if err != nil {
194		t.Skipf("MkdirAll failed: %v", err)
195	}
196	err = os.Chdir(dirname)
197	if err != nil {
198		t.Skipf("Chdir failed: %v", err)
199	}
200	// Change out of the temporary directory so that we don't inhibit its
201	// removal during test cleanup.
202	defer os.Chdir(`\`)
203
204	syscall.Getwd()
205}
206
207func TestGetStartupInfo(t *testing.T) {
208	var si syscall.StartupInfo
209	err := syscall.GetStartupInfo(&si)
210	if err != nil {
211		// see https://go.dev/issue/31316
212		t.Fatalf("GetStartupInfo: got error %v, want nil", err)
213	}
214}
215
216func FuzzUTF16FromString(f *testing.F) {
217	f.Add("hi")           // ASCII
218	f.Add("â")            // latin1
219	f.Add("ねこ")           // plane 0
220	f.Add("��")            // extra Plane 0
221	f.Add("\x90")         // invalid byte
222	f.Add("\xe3\x81")     // truncated
223	f.Add("\xe3\xc1\x81") // invalid middle byte
224
225	f.Fuzz(func(t *testing.T, tst string) {
226		res, err := syscall.UTF16FromString(tst)
227		if err != nil {
228			if strings.Contains(tst, "\x00") {
229				t.Skipf("input %q contains a NUL byte", tst)
230			}
231			t.Fatalf("UTF16FromString(%q): %v", tst, err)
232		}
233		t.Logf("UTF16FromString(%q) = %04x", tst, res)
234
235		if len(res) < 1 || res[len(res)-1] != 0 {
236			t.Fatalf("missing NUL terminator")
237		}
238		if len(res) > len(tst)+1 {
239			t.Fatalf("len(%04x) > len(%q)+1", res, tst)
240		}
241	})
242}
243