Skip to content

Turn off PIE for Alpine Linux on platforms other than amd64 and s390x#9456

Merged
xavierleroy merged 1 commit intoocaml:trunkfrom
xavierleroy:no-pie-3
Apr 18, 2020
Merged

Turn off PIE for Alpine Linux on platforms other than amd64 and s390x#9456
xavierleroy merged 1 commit intoocaml:trunkfrom
xavierleroy:no-pie-3

Conversation

@xavierleroy
Copy link
Copy Markdown
Contributor

This is a fix for issue #7562.

Alpine Linux produces position-independent executables (PIEs) by default. If non-PIC object files are given to the linker, it silently produces a wrong executable that crashes when run. This is the case for camlopt-generated code, which by default is not PIC except on amd64 (x86_64) and s390x (Z systems).

The fix is to special-case Alpine, or more exactly the -linux-musl system name that characterizes Linux systems using the Musl standard library, like Alpine, and add -no-pie to the C compiler options, except on amd64 and s390x.

It's on purpose that this fix is minimal and specific to Alpine. For instance, I thought about adding -no-pie for all targets except amd64 and s390x, but decided not to:

  1. -no-pie is not always necessary. E.g. Ubuntu >= 17.10 produces PIE by default, yet has no problems with non-PIC i386 code produced by ocamlopt, probably because the dynamic loader of Glibc is more clever than that of Musl.
  2. PIE is a security feature, so we should not turn it off until provably needed.

@xavierleroy
Copy link
Copy Markdown
Contributor Author

FYI: this passes CI precheck, and I manually checked that it fixes #7562 on Alpine i386.

Copy link
Copy Markdown
Member

@gasche gasche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The testing makes us confident that this should go in, so I'm approving.

A nitpicking note on the documentation comments (thanks for the comments!): in the comment you only talk about Alpine and explain the *-linux-musl) pattern as saying that it detects Alpine. I found the explanation you give in the PR more useful: we are not detecting Alpine but distributions built on the musl toolchain (of which only Alpine is definitely known to exhibit the problem). This is a more general class¹, and it gives a better information on what the actual incompatibility seems to be.

¹: most of the other distributions mentioned on the musl wiki sound pretty obscure to me, but in this container age you never know who will make it big.

@avsm
Copy link
Copy Markdown
Member

avsm commented Apr 17, 2020

The odd thing is that the musl cross-compile variants in opam (used to generate statically linked Linux binaries that can be used in any distro) work without a -no-pie; https://github.com/ocaml/opam-repository/blob/master/packages/ocaml-variants/ocaml-variants.4.10.0+musl+flambda/opam

Is it just Alpine that mandates PIE executables, or a constraint put on by the use of musl? My worry is that this patch will cause the opposite breakage (non-pie system libraries linking with pie OCaml object files) if some of those other distros are not PIE-default toolchains.

…390x

Alpine Linux and perhaps other musl-based Linux distributions produce
position-independent executables (PIEs) by default.  If non-PIC object
files are given to the linker, it silently produces a wrong executable
that crashes when run.  This is the case for ocamlopt-generated code,
which by default is not PIC except on amd64 (x86_64) and s390x (Z systems).

Closes: ocaml#7562
@xavierleroy
Copy link
Copy Markdown
Contributor Author

@gasche: I reworded the comment and commit message along the lines you suggested, and added a Changes entry.

@xavierleroy
Copy link
Copy Markdown
Contributor Author

@avsm: I can't answer your question because I don't know how those "musl cross-compile variants in opam" are built: how they report themselves via config.guess? what compiler toolchain do they use?

What the patch does is to select -no-pie if 1- the system reports itself as ...-linux-musl, and 2- it is not amd64 nor s390x. If the system is not PIE by default already, -no-pie should make no difference. If the system is amd64, this PR will make no difference.

Whether the system is PIE by default is, I believe, determined by the C compiler toolchain, i.e. how gcc or clang was configured.

Because of your concern I'm not merging yet. Could you suggest an experiment to check that this PR doesn't break your musl-flavored OPAM variants?

@avsm
Copy link
Copy Markdown
Member

avsm commented Apr 17, 2020

Just checked, and this patch looks safe for that usecase indeed. The opam compiler variants set CC to musl-gcc -Os and ASPP to musl-gcc -c (but for some reason, not AS). Therefore the configure script for that compiler runs on the host as normal and will pick up the correct PIE/no-PIE options.

(I spot a number of inconsistencies in opam-repository itself about this behaviour that'll we'll need to look at in more detail. In particular ocaml/opam-repository@0df7057#diff-51b7ebda0d57bff186c0634a3b4e878e is only applied to the 4.10+musl+static+flambda and not the 4.10+musl+flambda package. And the lack of overriding AS is odd. But that's all got nothing to do with this PR!)

@gasche
Copy link
Copy Markdown
Member

gasche commented Apr 17, 2020

@avsm speaking of AS vs. ASPP (still unrelated to this current PR, sorry), you may be interested in #9437.

@gasche
Copy link
Copy Markdown
Member

gasche commented Apr 17, 2020

@xavierleroy please feel free to merge whenever the CI returns green.

Should we maybe consider backporting this PR in earlier releases? "Alpine" is specific in that it is the default choice for containers, and people tend to try to run "old" versions of software in containers (for some people it is the point of containers, to freeze the world), so I can see people deciding to use older OCaml versions on cloud arm machines in the future, and hitting the issue.

@xavierleroy
Copy link
Copy Markdown
Contributor Author

xavierleroy commented Apr 18, 2020

The odd thing is that the musl cross-compile variants in opam (used to generate statically linked Linux binaries that can be used in any distro) work without a -no-pie; https://github.com/ocaml/opam-repository/blob/master/packages/ocaml-variants/ocaml-variants.4.10.0+musl+flambda/opam

On x86_64, sure, but on i386 I think it's broken already.

Here is my experiment: starting with Debian 10 i386, install musl-tools, configure OCaml like this OPAM switch does:

 ./configure CC="musl-gcc -Os" ASPP="musl-gcc -c"

Lo and behold, the generated ocamlc.opt is a PIE and crashes when run.

config,guess reports i686-pc-linux-gnu, so my PR would not add -no-pie.

Adding -no-pie manually works:

./configure CC="musl-gcc -no-pie -Os" ASPP="musl-gcc -c"

If I add -static the way the musl-static OPAM package does it:

./configure CC="musl-gcc -Os" ASPP="musl-gcc -c" LIBS="-static"

the generated ocamlc.opt is still a PIE, not a statically-linked executable, and still crashes.

Bottom line: the musl OPAM variants are broken on i386 and probably most other non-x86-64 platforms. This PR doesn't make things worse, but doesn't magically fix the OPAM variants either.

@gasche
Copy link
Copy Markdown
Member

gasche commented Apr 18, 2020

Totally random question: idly browsing the web on related matters (I was curious if other groups had run into this limitation of musl), I see discussions of -static -pie or (in recent versions of GCC only) -static-pie options. Is there a chance that using those could make some aspects of this thing nicer?

@xavierleroy
Copy link
Copy Markdown
Contributor Author

I realize that -static and -pie/-no-pie are mostly orthogonal: -static means that all libraries are pre-linked in advance and will not be dynamically loaded at start-up; -pie means that the executable is position-independent and can be relocated to random addresses at start-up.

So, to fix the musl-static OPAM variants, -no-pie -static should work. Let me try right now.

@xavierleroy
Copy link
Copy Markdown
Contributor Author

Here is the verdict for

./configure CC="musl-gcc -no-pie -Os" ASPP="musl-gcc -c" LIBS="-static"

It builds without crashing, but the generated ocamlc.opt and ocamlopt.opt are NOT statically linked.

It turns out that ./configure LIBS="-static" does not work: no -static option is given to the C compiler by ocamlopt at link-time.

One more thing to fix in these musl OPAM variants, I'm afraid. Does anyone use them?

@xavierleroy xavierleroy merged commit da12974 into ocaml:trunk Apr 18, 2020
@xavierleroy xavierleroy deleted the no-pie-3 branch April 18, 2020 09:16
@avsm
Copy link
Copy Markdown
Member

avsm commented Apr 18, 2020

They do work on x86_64; the way to make them generate a static executable is by (in the case of dune) passing a workspace override to add -static. For example this is done in the ocurrent/ocaml-ci.

Note that the vast, vast majority of container uses are x86_64, especially for static Linux binaries. There is little enough distro variation on aarch32/64 that it's easy enough to just build dynamic Linux binaries there per-distro. So with that in mind, I don't think it's really worth backporting this to earlier OCaml versions.

I am, however, now working on getting us i386 container images as part of our container CI matrix. The only 32-bit image we currently build is arm32v7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants