Skip to content

Commit

Permalink
cmd/link: enable ASLR on windows binaries built with -buildmode=c-shared
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Cherry Zhang <[email protected]>
Trust: Alex Brainman <[email protected]>
Trust: Cherry Zhang <[email protected]>
Run-TryBot: Alex Brainman <[email protected]>
TryBot-Result: Go Bot <[email protected]>
  • Loading branch information
qmuntal authored and cherrymui committed Oct 1, 2020
1 parent 7347907 commit 56dac60
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 7 deletions.
70 changes: 70 additions & 0 deletions src/cmd/link/internal/ld/ld_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package ld

import (
"debug/pe"
"fmt"
"internal/testenv"
"io/ioutil"
Expand Down Expand Up @@ -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")
}
}
24 changes: 17 additions & 7 deletions src/cmd/link/internal/ld/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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")
Expand All @@ -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")
Expand Down
6 changes: 6 additions & 0 deletions src/cmd/link/internal/ld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 56dac60

Please sign in to comment.