From 3fc8a90213279d16ac1da1ec2ddb384240c88dc4 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Wed, 24 Feb 2021 10:55:41 +0100 Subject: [PATCH 1/4] cmd/link: support full relro Most Linux distributions today enable PIE and full RELRO on all binaries to make exploitation harder. When buildmode=pie is used we enable full relro as that is probably what most people want regardless. This introduces a negligible startup time for binaries. https://fedoraproject.org/wiki/Changes/Harden_All_Packages https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro Related #44480 --- src/cmd/go/internal/work/security.go | 2 +- src/cmd/go/internal/work/security_test.go | 3 +++ src/cmd/link/doc.go | 2 ++ src/cmd/link/internal/ld/lib.go | 19 +++++++++++++++---- src/cmd/link/internal/ld/main.go | 1 + 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go index 0bf8763543b14e..23e75c895076d0 100644 --- a/src/cmd/go/internal/work/security.go +++ b/src/cmd/go/internal/work/security.go @@ -207,7 +207,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{ re(`-Wl,--(no-)?warn-([^,]+)`), re(`-Wl,-?-wrap[=,][^,@\-][^,]*`), re(`-Wl,-z,(no)?execstack`), - re(`-Wl,-z,relro`), + re(`-Wl,-z,relro(,-z,now)?`), re(`[a-zA-Z0-9_/].*\.(a|o|obj|dll|dylib|so|tbd)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o) re(`\./.*\.(a|o|obj|dll|dylib|so|tbd)`), diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go index d2aeb54e0cea14..85553db040baa9 100644 --- a/src/cmd/go/internal/work/security_test.go +++ b/src/cmd/go/internal/work/security_test.go @@ -150,6 +150,8 @@ var goodLinkerFlags = [][]string{ {"-Wl,--just-symbols,foo"}, {"-Wl,--warn-error"}, {"-Wl,--no-warn-error"}, + {"-Wl,-z,relro"}, + {"-Wl,-z,relro,-z,now"}, {"foo.so"}, {"_世界.dll"}, {"./x.o"}, @@ -227,6 +229,7 @@ var badLinkerFlags = [][]string{ {"-Wl,-R,foo,bar"}, {"-Wl,-R,@foo"}, {"-Wl,--just-symbols,@foo"}, + {"-Wl,-z,relro,-z,nottoday"}, {"../x.o"}, } diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go index a5701327141ecb..6f4546205eb44e 100644 --- a/src/cmd/link/doc.go +++ b/src/cmd/link/doc.go @@ -87,6 +87,8 @@ Flags: instead of $GOROOT/pkg/$GOOS_$GOARCH. -k symbol Set field tracking symbol. Use this flag when GOEXPERIMENT=fieldtrack is set. + -l + Disable Full RELRO. -libgcc file Set name of compiler support library. This is only used in internal link mode. diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 04b2556ea1ecb6..a4acc1bf8c2451 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1404,6 +1404,17 @@ func (ctxt *Link) hostlink() { return argv } + // Enables Full/Partial RELRO. + addRELROargs := func(argv []string) []string { + relro := "-Wl,-z,relro" + // Enable Full RELRO + if !*FlagL { + relro += ",-z,now" + } + argv = append(argv, relro) + return argv + } + switch ctxt.BuildMode { case BuildModeExe: if ctxt.HeadType == objabi.Hdarwin { @@ -1420,7 +1431,7 @@ func (ctxt *Link) hostlink() { default: // ELF. if ctxt.UseRelro() { - argv = append(argv, "-Wl,-z,relro") + argv = addRELROargs(argv) } argv = append(argv, "-pie") } @@ -1429,7 +1440,7 @@ func (ctxt *Link) hostlink() { argv = append(argv, "-dynamiclib") } else { if ctxt.UseRelro() { - argv = append(argv, "-Wl,-z,relro") + argv = addRELROargs(argv) } argv = append(argv, "-shared") if ctxt.HeadType == objabi.Hwindows { @@ -1444,7 +1455,7 @@ func (ctxt *Link) hostlink() { } case BuildModeShared: if ctxt.UseRelro() { - argv = append(argv, "-Wl,-z,relro") + argv = addRELROargs(argv) } argv = append(argv, "-shared") case BuildModePlugin: @@ -1452,7 +1463,7 @@ func (ctxt *Link) hostlink() { argv = append(argv, "-dynamiclib") } else { if ctxt.UseRelro() { - argv = append(argv, "-Wl,-z,relro") + argv = addRELROargs(argv) } argv = append(argv, "-shared") } diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index c52e6e909d8476..fce169f37f2651 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -89,6 +89,7 @@ var ( flagN = flag.Bool("n", false, "dump symbol table") FlagS = flag.Bool("s", false, "disable symbol table") FlagW = flag.Bool("w", false, "disable DWARF generation") + FlagL = flag.Bool("l", false, "disable full RELRO") flag8 bool // use 64-bit addresses in symbol table flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") From cfeecf906ab18622f99e4a90aaf0100e58d98efb Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Thu, 22 Apr 2021 20:59:45 +0200 Subject: [PATCH 2/4] cmd/go: Remove -Wl,-z,relro,-z,now test case Signed-off-by: Morten Linderud --- src/cmd/go/internal/work/security.go | 2 +- src/cmd/go/internal/work/security_test.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go index 23e75c895076d0..0bf8763543b14e 100644 --- a/src/cmd/go/internal/work/security.go +++ b/src/cmd/go/internal/work/security.go @@ -207,7 +207,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{ re(`-Wl,--(no-)?warn-([^,]+)`), re(`-Wl,-?-wrap[=,][^,@\-][^,]*`), re(`-Wl,-z,(no)?execstack`), - re(`-Wl,-z,relro(,-z,now)?`), + re(`-Wl,-z,relro`), re(`[a-zA-Z0-9_/].*\.(a|o|obj|dll|dylib|so|tbd)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o) re(`\./.*\.(a|o|obj|dll|dylib|so|tbd)`), diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go index 85553db040baa9..d2aeb54e0cea14 100644 --- a/src/cmd/go/internal/work/security_test.go +++ b/src/cmd/go/internal/work/security_test.go @@ -150,8 +150,6 @@ var goodLinkerFlags = [][]string{ {"-Wl,--just-symbols,foo"}, {"-Wl,--warn-error"}, {"-Wl,--no-warn-error"}, - {"-Wl,-z,relro"}, - {"-Wl,-z,relro,-z,now"}, {"foo.so"}, {"_世界.dll"}, {"./x.o"}, @@ -229,7 +227,6 @@ var badLinkerFlags = [][]string{ {"-Wl,-R,foo,bar"}, {"-Wl,-R,@foo"}, {"-Wl,--just-symbols,@foo"}, - {"-Wl,-z,relro,-z,nottoday"}, {"../x.o"}, } From b2601e7c89c7e70f7d48fb77ec418c9d94560381 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Thu, 22 Apr 2021 21:02:52 +0200 Subject: [PATCH 3/4] cmd/link: change flag from -l to -relro={true,false} Signed-off-by: Morten Linderud --- src/cmd/link/doc.go | 4 ++-- src/cmd/link/internal/ld/lib.go | 2 +- src/cmd/link/internal/ld/main.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go index 6f4546205eb44e..aed61c4f8f78da 100644 --- a/src/cmd/link/doc.go +++ b/src/cmd/link/doc.go @@ -87,8 +87,8 @@ Flags: instead of $GOROOT/pkg/$GOOS_$GOARCH. -k symbol Set field tracking symbol. Use this flag when GOEXPERIMENT=fieldtrack is set. - -l - Disable Full RELRO. + -relro + Enable RELRO (default true). -libgcc file Set name of compiler support library. This is only used in internal link mode. diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index a4acc1bf8c2451..fc95de179da62a 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1408,7 +1408,7 @@ func (ctxt *Link) hostlink() { addRELROargs := func(argv []string) []string { relro := "-Wl,-z,relro" // Enable Full RELRO - if !*FlagL { + if *FlagRelro { relro += ",-z,now" } argv = append(argv, relro) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index fce169f37f2651..bb9c02f43437d6 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -89,13 +89,13 @@ var ( flagN = flag.Bool("n", false, "dump symbol table") FlagS = flag.Bool("s", false, "disable symbol table") FlagW = flag.Bool("w", false, "disable DWARF generation") - FlagL = flag.Bool("l", false, "disable full RELRO") flag8 bool // use 64-bit addresses in symbol table flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size") flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph") FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") + FlagRelro = flag.Bool("relro", true, "enable RELRO for buildmode=pie") FlagRound = flag.Int("R", -1, "set address rounding `quantum`") FlagTextAddr = flag.Int64("T", -1, "set text segment `address`") flagEntrySymbol = flag.String("E", "", "set `entry` symbol name") From b85c7f7ebe886d197d998b170a542920d4a02bf7 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Fri, 6 May 2022 11:30:12 +0200 Subject: [PATCH 4/4] cmd/link: change flag from -relro to -bindnow Signed-off-by: Morten Linderud --- src/cmd/link/doc.go | 5 +++-- src/cmd/link/internal/ld/lib.go | 2 +- src/cmd/link/internal/ld/main.go | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go index aed61c4f8f78da..4678a6c82c7b51 100644 --- a/src/cmd/link/doc.go +++ b/src/cmd/link/doc.go @@ -87,8 +87,9 @@ Flags: instead of $GOROOT/pkg/$GOOS_$GOARCH. -k symbol Set field tracking symbol. Use this flag when GOEXPERIMENT=fieldtrack is set. - -relro - Enable RELRO (default true). + -bindnow + When linking externally and marking variables read-only after relocation, also require + immediate function binding (default true) -libgcc file Set name of compiler support library. This is only used in internal link mode. diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index fc95de179da62a..4d3d8c3edce215 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1408,7 +1408,7 @@ func (ctxt *Link) hostlink() { addRELROargs := func(argv []string) []string { relro := "-Wl,-z,relro" // Enable Full RELRO - if *FlagRelro { + if *FlagBindNow { relro += ",-z,now" } argv = append(argv, relro) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index bb9c02f43437d6..2f3c613d3040cf 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -95,7 +95,7 @@ var ( FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size") flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph") FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") - FlagRelro = flag.Bool("relro", true, "enable RELRO for buildmode=pie") + FlagBindNow = flag.Bool("bindnow", true, "bind function calls when linking externally") FlagRound = flag.Int("R", -1, "set address rounding `quantum`") FlagTextAddr = flag.Int64("T", -1, "set text segment `address`") flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")