]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: enable ASLR on windows binaries built with -buildmode=c-shared
authorQuim <quimmuntal@gmail.com>
Wed, 16 Sep 2020 23:59:14 +0000 (01:59 +0200)
committerCherry Zhang <cherryyz@google.com>
Thu, 1 Oct 2020 14:26:35 +0000 (14:26 +0000)
Windows binaries built with -buildmode=c-shared set will have
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag set, and
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag set for windows/amd64.

ASLR can be disabled on windows by using the new linker -aslr flag.

RELNOTE=yes

Fixes #41421

Change-Id: I62bd88c6d7e0f87173b093a0ad8e1a4d269ec790
Reviewed-on: https://go-review.googlesource.com/c/go/+/255259
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Alex Brainman <alex.brainman@gmail.com>
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>

src/cmd/link/internal/ld/ld_test.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/main.go

index db339b484d83df8aff44ed4820fca9cea89d1f73..4367c1028ead111cf0c3d73bfe0b38320c9daf47 100644 (file)
@@ -5,6 +5,7 @@
 package ld
 
 import (
+       "debug/pe"
        "fmt"
        "internal/testenv"
        "io/ioutil"
@@ -167,3 +168,72 @@ func TestPPC64LargeTextSectionSplitting(t *testing.T) {
                t.Fatal(err)
        }
 }
+
+func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
+       platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
+       switch platform {
+       case "windows/amd64", "windows/386":
+       default:
+               t.Skip("skipping windows amd64/386 only test")
+       }
+
+       t.Run("aslr", func(t *testing.T) {
+               testWindowsBuildmodeCSharedASLR(t, true)
+       })
+       t.Run("no-aslr", func(t *testing.T) {
+               testWindowsBuildmodeCSharedASLR(t, false)
+       })
+}
+
+func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
+       t.Parallel()
+       testenv.MustHaveGoBuild(t)
+
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       srcfile := filepath.Join(dir, "test.go")
+       objfile := filepath.Join(dir, "test.dll")
+       if err := ioutil.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
+               t.Fatal(err)
+       }
+       argv := []string{"build", "-buildmode=c-shared"}
+       if !useASLR {
+               argv = append(argv, "-ldflags", "-aslr=false")
+       }
+       argv = append(argv, "-o", objfile, srcfile)
+       out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
+       if err != nil {
+               t.Fatalf("build failure: %s\n%s\n", err, string(out))
+       }
+
+       f, err := pe.Open(objfile)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer f.Close()
+       var dc uint16
+       switch oh := f.OptionalHeader.(type) {
+       case *pe.OptionalHeader32:
+               dc = oh.DllCharacteristics
+       case *pe.OptionalHeader64:
+               dc = oh.DllCharacteristics
+               hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
+               if useASLR && !hasHEVA {
+                       t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
+               } else if !useASLR && hasHEVA {
+                       t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
+               }
+       default:
+               t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
+       }
+       hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
+       if useASLR && !hasASLR {
+               t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
+       } else if !useASLR && hasASLR {
+               t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
+       }
+}
index b2ca658c3ce6c8e39405b05c9e252292c3c09c39..0cce98a4479779f79bda4112dd352372b56005dd 100644 (file)
@@ -1290,6 +1290,17 @@ func (ctxt *Link) hostlink() {
                argv = append(argv, "-Wl,-bbigtoc")
        }
 
+       // Enable ASLR on Windows.
+       addASLRargs := func(argv []string) []string {
+               // Enable ASLR.
+               argv = append(argv, "-Wl,--dynamicbase")
+               // enable high-entropy ASLR on 64-bit.
+               if ctxt.Arch.PtrSize >= 8 {
+                       argv = append(argv, "-Wl,--high-entropy-va")
+               }
+               return argv
+       }
+
        switch ctxt.BuildMode {
        case BuildModeExe:
                if ctxt.HeadType == objabi.Hdarwin {
@@ -1302,12 +1313,7 @@ func (ctxt *Link) hostlink() {
                switch ctxt.HeadType {
                case objabi.Hdarwin, objabi.Haix:
                case objabi.Hwindows:
-                       // Enable ASLR.
-                       argv = append(argv, "-Wl,--dynamicbase")
-                       // enable high-entropy ASLR on 64-bit.
-                       if ctxt.Arch.PtrSize >= 8 {
-                               argv = append(argv, "-Wl,--high-entropy-va")
-                       }
+                       argv = addASLRargs(argv)
                        // Work around binutils limitation that strips relocation table for dynamicbase.
                        // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
                        argv = append(argv, "-Wl,--export-all-symbols")
@@ -1331,7 +1337,11 @@ func (ctxt *Link) hostlink() {
                                argv = append(argv, "-Wl,-z,relro")
                        }
                        argv = append(argv, "-shared")
-                       if ctxt.HeadType != objabi.Hwindows {
+                       if ctxt.HeadType == objabi.Hwindows {
+                               if *flagAslr {
+                                       argv = addASLRargs(argv)
+                               }
+                       } else {
                                // Pass -z nodelete to mark the shared library as
                                // non-closeable: a dlclose will do nothing.
                                argv = append(argv, "-Wl,-z,nodelete")
index 6f4ccbfb7a11be4d0910819102a0867fb88ad458..0e030218c51b750704619d6fbc7d1758ea7315f0 100644 (file)
@@ -65,6 +65,7 @@ var (
        flagDumpDep       = flag.Bool("dumpdep", false, "dump symbol dependency graph")
        flagRace          = flag.Bool("race", false, "enable race detector")
        flagMsan          = flag.Bool("msan", false, "enable MSan interface")
+       flagAslr          = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows")
 
        flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
        flagLibGCC     = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
@@ -157,6 +158,11 @@ func Main(arch *sys.Arch, theArch Arch) {
                ctxt.HeadType.Set(objabi.GOOS)
        }
 
+       if !*flagAslr && ctxt.BuildMode != BuildModeCShared {
+               Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared")
+               usage()
+       }
+
        checkStrictDups = *FlagStrictDups
 
        startProfile()