Alpine Linux requires PIE as default buildmode

986 views
Skip to first unread message

wei xiao

unread,
Nov 1, 2016, 8:33:15 AM11/1/16
to golang-dev
As you know, for security enhancement, all Alpine userland binaries are compiled as Position Independent Executables (PIE) with stack smashing protection (https://www.alpinelinux.org/about/).
But GoLang default buildmode is "exe" and it will cause compatability issue when deploying GoLang program in Alpine platform without explictly changing buildmode.
In fact, this situation is similar to Android PIE requirement and GoLang switch its default buildmode to "pie" if target platform is Android.
I suggest adding a platform "alpine" as shown below to handle the PIE issue and user can set "GOOS" env var to enable it.

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -365,7 +365,8 @@ func buildModeInit() {
                ldBuildmode = "c-shared"
        case "default":
                switch platform {
-               case "android/arm", "android/arm64", "android/amd64", "android/386":
+               case "android/arm", "android/arm64", "android/amd64", "android/386",
+                       "alpine/arm", "alpine/arm64", "alpine/amd64", "alpine/386":
                        codegenArg = "-shared"
                        ldBuildmode = "pie"

Brad Fitzpatrick

unread,
Nov 1, 2016, 8:47:25 AM11/1/16
to wei xiao, golang-dev
Could you file a bug with details? I don't think adding a new GOOS=alpine value will be the answer, though. GOOS=android is weird enough as it is.


--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

william....@gmail.com

unread,
Nov 7, 2016, 12:17:20 PM11/7/16
to golang-dev, wei.will...@gmail.com, Wei....@arm.com
With further analysis, i find that it should be a Go compatibility issue to external linker (for ARM64 and MIPS64).
In Alpine and other various security hardened Linux distros, GCC toolchain enable PIE by default and user needs to explicitly pass "-no-pie" (but unrecognized by older gcc) to turn it off.
But Golang compile object file with buildmode="exe" by default and generate an abnormal executable file with invoking external linker which enable PIE by default.
Recently GCC upstream started supporting default PIE and the compatibility issue expects to appear in more Linux distros in future.
Besides previous solution of adding a new GOOS, i suggest following three solutions. Any comments?

Solution 1
Change default buildmode from "exe" to "pie" (but it will impact all platforms and os)

Solution 2
Provide a configuration for user to customize Golang default buildmode. e.g., use environment variable as below
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -214,7 +214,13 @@ func addBuildFlags(cmd *Command) {

        cmd.Flag.Var((*stringsFlag)(&buildAsmflags), "asmflags", "")
        cmd.Flag.Var(buildCompiler{}, "compiler", "")
-       cmd.Flag.StringVar(&buildBuildmode, "buildmode", "default", "")
+
+       if bm := os.Getenv("GOBUILDMODE"); bm != "" {
+               cmd.Flag.StringVar(&buildBuildmode, "buildmode", bm, "")
+       } else {
+               cmd.Flag.StringVar(&buildBuildmode, "buildmode", "default", "")
+       }
+
        cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "")
        cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "")
        cmd.Flag.StringVar(&buildContext.InstallSuffix, "installsuffix", "", "")

(user needs to set GOBUILDMODE="pie" for Alpine and other security hardened Linux distros)

Solution 3
Turn PIE off explicitly for "exe" buildmode by detecting the flag toolchain can recognize as below
diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go
index 366b6c0..99569b7 100644
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -68,6 +68,9 @@ func mkEnv() []envVar {
                env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")})
                cmd = b.gxxCmd(".")
                env = append(env, envVar{"CXX", cmd[0]})
+               if b.gccSupportsNoPie() {
+                       env = append(env, envVar{"GOGCCNOPIE", "-no-pie"})
+               }
        }

        if buildContext.CgoEnabled {
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 629facd..b5c5638 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1000,6 +1000,9 @@ func (l *Link) hostlink() {
                if Headtype == obj.Hdarwin {
                        argv = append(argv, "-Wl,-pagezero_size,4000000")
                }
+               if nopieflags := os.Getenv("GOGCCNOPIE"); nopieflags != "" {
+                       argv = append(argv, nopieflags)
+               }

(This solution won't impact existing Linux distros but can fix crash issue in Alpine and other distros with PIE enabled by default)

To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

Ian Lance Taylor

unread,
Nov 7, 2016, 1:21:26 PM11/7/16
to william....@gmail.com, golang-dev, wei.will...@gmail.com, Wei....@arm.com
On Mon, Nov 7, 2016 at 12:17 AM, Wei....@arm.com
<william....@gmail.com> wrote:
> With further analysis, i find that it should be a Go compatibility issue to
> external linker (for ARM64 and MIPS64).
> In Alpine and other various security hardened Linux distros, GCC toolchain
> enable PIE by default and user needs to explicitly pass "-no-pie" (but
> unrecognized by older gcc) to turn it off.
> But Golang compile object file with buildmode="exe" by default and generate
> an abnormal executable file with invoking external linker which enable PIE
> by default.
> Recently GCC upstream started supporting default PIE and the compatibility
> issue expects to appear in more Linux distros in future.
> Besides previous solution of adding a new GOOS, i suggest following three
> solutions. Any comments?

I don't fully understand the problem.

What actually goes wrong? Are you saying that Alpine Linux rejects a
non-PIE executable?

Ian

william....@gmail.com

unread,
Nov 8, 2016, 12:35:46 AM11/8/16
to golang-dev, william....@gmail.com, wei.will...@gmail.com, Wei....@arm.com
Not reject but the generated executable is abnormal and will crash at runtime.
Let's explain it with an example:
suppose we compile hello.go with: go build hello.go
then go will invoke compile with: compile -o hello.a hello.go xxx
and invoke link with: link -extld=gcc -buildmode=exe hello.a xxx
On ARM64 and MIPS64, link will finally invoke gcc with: gcc -o hello -rdynamic hello.o xxx
if gcc enable PIE by default then the executable "hello" is abnormal since hello.o is generated by go link for "exe" buildmode

For PIE default gcc, flag "-no-pie" is needed to disable PIE for go "exe" buildmode to generate correct executable.

Ian Lance Taylor

unread,
Nov 8, 2016, 1:09:29 AM11/8/16
to william....@gmail.com, golang-dev, wei.will...@gmail.com, Wei....@arm.com
On Mon, Nov 7, 2016 at 9:35 PM, Wei....@arm.com
<william....@gmail.com> wrote:
> Not reject but the generated executable is abnormal and will crash at
> runtime.
> Let's explain it with an example:
> suppose we compile hello.go with: go build hello.go
> then go will invoke compile with: compile -o hello.a hello.go xxx
> and invoke link with: link -extld=gcc -buildmode=exe hello.a xxx
> On ARM64 and MIPS64, link will finally invoke gcc with: gcc -o hello
> -rdynamic hello.o xxx
> if gcc enable PIE by default then the executable "hello" is abnormal since
> hello.o is generated by go link for "exe" buildmode
>
> For PIE default gcc, flag "-no-pie" is needed to disable PIE for go "exe"
> buildmode to generate correct executable.

Thanks. So if I understand correctly, this means that whenever we are
using external link mode with -buildmode=exe, we need to pass -no-pie
to the external linker if it is supported. We should probably change
setextld in cmd/go/build.go to do that.

I suggest that you open an issue or send a patch. Thanks.

Ian

william....@gmail.com

unread,
Nov 8, 2016, 4:15:08 AM11/8/16
to golang-dev, william....@gmail.com, wei.will...@gmail.com, Wei....@arm.com
Yes, I have open an issue for it: https://github.com/golang/go/issues/17847

As you said, Change seextld can fix the compatibility issue. But how about solution 2?
For some security hardened Linux distros (such as Alpine), "exe" buildmode means disabling security feature such as ASLR
It would be better if GoLang can provide a configure method to change default buildmode for customizing need (such as set to "pie" for security)

william....@gmail.com

unread,
Nov 8, 2016, 4:26:55 AM11/8/16
to golang-dev, william....@gmail.com, wei.will...@gmail.com, Wei....@arm.com
or combine providing the configuration method to modify default buildmode (solution 2) with changing seextld together

Ian Lance Taylor

unread,
Nov 8, 2016, 9:18:42 AM11/8/16
to william....@gmail.com, golang-dev, wei.will...@gmail.com, Wei....@arm.com
On Tue, Nov 8, 2016 at 1:15 AM, Wei....@arm.com
<william....@gmail.com> wrote:
> Yes, I have open an issue for it: https://github.com/golang/go/issues/17847
>
> As you said, Change seextld can fix the compatibility issue. But how about
> solution 2?
> For some security hardened Linux distros (such as Alpine), "exe" buildmode
> means disabling security feature such as ASLR
> It would be better if GoLang can provide a configure method to change
> default buildmode for customizing need (such as set to "pie" for security)

I've replied on the bug. I realized that I still don't understand
what is going on.

There are two different things here. One is making sure that the go
tool works correctly when the go tool is generating a normal non-PIE
executable and the external linker defaults to PIE. The other is
changing the default go tool build mode. Those are two different
things. The first is a bug. The second is a user interface choice.
Let's fix the bug first.

I'll note that ASLR seems to me to be less important in a language
like Go in which buffer overruns are impossible. And on the one hand
we don't want more knobs, and on the other hand defaulting to PIE
requires the external linker on some platforms. I'm not sure what the
best approach is. But I am sure that we should fix the bug.

Ian
Reply all
Reply to author
Forward
0 new messages