diff options
Diffstat (limited to 'src/runtime/syscall_windows_test.go')
-rw-r--r-- | src/runtime/syscall_windows_test.go | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go index 79807035cc..3f350cec27 100644 --- a/src/runtime/syscall_windows_test.go +++ b/src/runtime/syscall_windows_test.go @@ -7,6 +7,8 @@ package runtime_test import ( "bytes" "fmt" + "internal/syscall/windows/sysdll" + "internal/testenv" "io/ioutil" "os" "os/exec" @@ -771,3 +773,94 @@ func TestNumCPU(t *testing.T) { t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask) } } + +// See Issue 14959 +func TestDLLPreloadMitigation(t *testing.T) { + if _, err := exec.LookPath("gcc"); err != nil { + t.Skip("skipping test: gcc is missing") + } + + dir0, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + defer os.Chdir(dir0) + + const src = ` +#include <stdint.h> +#include <windows.h> + +uintptr_t cfunc() { + SetLastError(123); +} +` + tmpdir, err := ioutil.TempDir("", "TestDLLPreloadMitigation") + if err != nil { + t.Fatal("TempDir failed: ", err) + } + defer os.RemoveAll(tmpdir) + + srcname := "nojack.c" + err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) + if err != nil { + t.Fatal(err) + } + name := "nojack.dll" + cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname) + cmd.Dir = tmpdir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("failed to build dll: %v - %v", err, string(out)) + } + dllpath := filepath.Join(tmpdir, name) + + dll := syscall.MustLoadDLL(dllpath) + dll.MustFindProc("cfunc") + dll.Release() + + // Get into the directory with the DLL we'll load by base name + // ("nojack.dll") Think of this as the user double-clicking an + // installer from their Downloads directory where a browser + // silently downloaded some malicious DLLs. + os.Chdir(tmpdir) + + // First before we can load a DLL from the current directory, + // loading it only as "nojack.dll", without an absolute path. + delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly + dll, err = syscall.LoadDLL(name) + if err != nil { + t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err) + } + dll.Release() + + // And now verify that if we register it as a system32-only + // DLL, the implicit loading from the current directory no + // longer works. + sysdll.IsSystemDLL[name] = true + dll, err = syscall.LoadDLL(name) + if err == nil { + dll.Release() + if wantLoadLibraryEx() { + t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err) + } + t.Skip("insecure load of DLL, but expected") + } +} + +// wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests. +func wantLoadLibraryEx() bool { + return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce" +} + +func TestLoadLibraryEx(t *testing.T) { + use, have, flags := runtime.LoadLibraryExStatus() + if use { + return // success. + } + if wantLoadLibraryEx() { + t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)", + have, flags) + } + t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)", + have, flags) +} |