Skip to content

Commit 56dac60

Browse files
qmuntalcherrymui
authored andcommitted
cmd/link: enable ASLR on windows binaries built with -buildmode=c-shared
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]>
1 parent 7347907 commit 56dac60

File tree

3 files changed

+93
-7
lines changed

3 files changed

+93
-7
lines changed

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package ld
66

77
import (
8+
"debug/pe"
89
"fmt"
910
"internal/testenv"
1011
"io/ioutil"
@@ -167,3 +168,72 @@ func TestPPC64LargeTextSectionSplitting(t *testing.T) {
167168
t.Fatal(err)
168169
}
169170
}
171+
172+
func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
173+
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
174+
switch platform {
175+
case "windows/amd64", "windows/386":
176+
default:
177+
t.Skip("skipping windows amd64/386 only test")
178+
}
179+
180+
t.Run("aslr", func(t *testing.T) {
181+
testWindowsBuildmodeCSharedASLR(t, true)
182+
})
183+
t.Run("no-aslr", func(t *testing.T) {
184+
testWindowsBuildmodeCSharedASLR(t, false)
185+
})
186+
}
187+
188+
func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
189+
t.Parallel()
190+
testenv.MustHaveGoBuild(t)
191+
192+
dir, err := ioutil.TempDir("", "go-build")
193+
if err != nil {
194+
t.Fatal(err)
195+
}
196+
defer os.RemoveAll(dir)
197+
198+
srcfile := filepath.Join(dir, "test.go")
199+
objfile := filepath.Join(dir, "test.dll")
200+
if err := ioutil.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
201+
t.Fatal(err)
202+
}
203+
argv := []string{"build", "-buildmode=c-shared"}
204+
if !useASLR {
205+
argv = append(argv, "-ldflags", "-aslr=false")
206+
}
207+
argv = append(argv, "-o", objfile, srcfile)
208+
out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
209+
if err != nil {
210+
t.Fatalf("build failure: %s\n%s\n", err, string(out))
211+
}
212+
213+
f, err := pe.Open(objfile)
214+
if err != nil {
215+
t.Fatal(err)
216+
}
217+
defer f.Close()
218+
var dc uint16
219+
switch oh := f.OptionalHeader.(type) {
220+
case *pe.OptionalHeader32:
221+
dc = oh.DllCharacteristics
222+
case *pe.OptionalHeader64:
223+
dc = oh.DllCharacteristics
224+
hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
225+
if useASLR && !hasHEVA {
226+
t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
227+
} else if !useASLR && hasHEVA {
228+
t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
229+
}
230+
default:
231+
t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
232+
}
233+
hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
234+
if useASLR && !hasASLR {
235+
t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
236+
} else if !useASLR && hasASLR {
237+
t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
238+
}
239+
}

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,17 @@ func (ctxt *Link) hostlink() {
12901290
argv = append(argv, "-Wl,-bbigtoc")
12911291
}
12921292

1293+
// Enable ASLR on Windows.
1294+
addASLRargs := func(argv []string) []string {
1295+
// Enable ASLR.
1296+
argv = append(argv, "-Wl,--dynamicbase")
1297+
// enable high-entropy ASLR on 64-bit.
1298+
if ctxt.Arch.PtrSize >= 8 {
1299+
argv = append(argv, "-Wl,--high-entropy-va")
1300+
}
1301+
return argv
1302+
}
1303+
12931304
switch ctxt.BuildMode {
12941305
case BuildModeExe:
12951306
if ctxt.HeadType == objabi.Hdarwin {
@@ -1302,12 +1313,7 @@ func (ctxt *Link) hostlink() {
13021313
switch ctxt.HeadType {
13031314
case objabi.Hdarwin, objabi.Haix:
13041315
case objabi.Hwindows:
1305-
// Enable ASLR.
1306-
argv = append(argv, "-Wl,--dynamicbase")
1307-
// enable high-entropy ASLR on 64-bit.
1308-
if ctxt.Arch.PtrSize >= 8 {
1309-
argv = append(argv, "-Wl,--high-entropy-va")
1310-
}
1316+
argv = addASLRargs(argv)
13111317
// Work around binutils limitation that strips relocation table for dynamicbase.
13121318
// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
13131319
argv = append(argv, "-Wl,--export-all-symbols")
@@ -1331,7 +1337,11 @@ func (ctxt *Link) hostlink() {
13311337
argv = append(argv, "-Wl,-z,relro")
13321338
}
13331339
argv = append(argv, "-shared")
1334-
if ctxt.HeadType != objabi.Hwindows {
1340+
if ctxt.HeadType == objabi.Hwindows {
1341+
if *flagAslr {
1342+
argv = addASLRargs(argv)
1343+
}
1344+
} else {
13351345
// Pass -z nodelete to mark the shared library as
13361346
// non-closeable: a dlclose will do nothing.
13371347
argv = append(argv, "-Wl,-z,nodelete")

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ var (
6565
flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph")
6666
flagRace = flag.Bool("race", false, "enable race detector")
6767
flagMsan = flag.Bool("msan", false, "enable MSan interface")
68+
flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows")
6869

6970
flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
7071
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) {
157158
ctxt.HeadType.Set(objabi.GOOS)
158159
}
159160

161+
if !*flagAslr && ctxt.BuildMode != BuildModeCShared {
162+
Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared")
163+
usage()
164+
}
165+
160166
checkStrictDups = *FlagStrictDups
161167

162168
startProfile()

0 commit comments

Comments
 (0)