New hardened build support (coming) in F16

Adam Jackson ajax at redhat.com
Mon Aug 8 19:15:08 UTC 2011


tl;dr version: If you have a security-sensitive package, and wish to 
enable some gcc-level hardening features with a modest performance 
impact, you will soon be able to enable them (nearly) automagically by 
rebuilding with this line in your spec file:

%define _hardened_build 1

Now for the details.

* 1: what are we trying to do?

There are three somewhat-overlapping build features in play here.  The 
first one is called "relro", which instructs the linker to emit some 
relocations in a special segment that can be marked read-only after 
relocation processing is finished but before you call into main().  Or 
in English: more things that you've asked to be const, will actually be 
const.  This on its own is quite cheap, and so it has been enabled 
globally as of redhat-rpm-config-9.1.0-13.fc16.

By default, not all symbols are resolved that early in program 
execution.  In particular, functions are resolved lazily the first time 
they're called.  This makes startup faster, and since not all functions 
are actually called in typical program execution, usually makes total 
execution time faster.  However, if all symbols were resolved early, the 
relro feature could do a better job, and virtually all relocations could 
be made read-only.  The '-z now' flag to the linker makes this happen, 
and an app so linked is said to be "Full RELRO" instead of "Partial RELRO".

Finally, applications may be built as position-independent executables, 
by passing -fPIC or -fPIE at build time and -pie at link time.  This 
allows the runtime linker to randomize the placement of the executable 
at runtime, which makes it more difficult for an attacker to guess the 
address of writeable memory.

* 2: how do we go about doing it?

The non-PIE parts of this are trivial, just pass the appropriate flags 
to the linker and you're done.  PIE is more difficult, both at build 
time and at link time.  Although both -fPIC and -fPIE produce 
position-independent code at the assembly level, -fPIE will (at least on 
amd64) produce relocation types that are only valid in an executable. 
This means you can't just say -fPIE in CFLAGS: your libraries will fail 
to link.  (PIC objects in a PIE executable are fine; PIE objects in a 
PIC library are not.  When in doubt, -fPIC.)

Likewise, at link time, the -pie and -shared options are mutually 
exclusive.  ld.gold will simply refuse to execute if you specify both. 
ld.bfd will (afaict) let whichever one comes last win, and if that 
happens to be -pie when you're building a shared library it will fail to 
link because it won't be able to find a _start symbol.

All of this is only an issue because most build systems don't let you 
say different CFLAGS or LDFLAGS for shared libraries and executables.  Sigh.

So instead, we'll teach gcc to figure it out.  To do this we'll use the 
-specs flag to pass some rewrite rules to the compiler driver.  At 
compile time, if we don't see -fPIC or -fPIE on the command line, we'll 
add -fPIC.  At link time, if we don't see -shared, we'll add -pie.  This 
way we build relocatable objects that are always suitable for either 
type of final link object, and we'll only attempt to build a PIE if we 
know we're not building a shared library.  Victory!

* 3: what does this mean for you?

The link-time bit of the last paragraph required a bit of gcc magic to 
get right (previously specs rules could only add strings to the command 
line of the program to invoke; they could not rewrite gcc's notion of 
which flags had been passed in the first place).  Thanks to a patch from 
Jakub Jelinek, this is now fixed in gcc-4.6.1-7.fc16, and will be in gcc 
4.7 and later.  As a result, %defined _hardened_build 1 will not work 
until that gcc update has gone through.

Once that's done (and redhat-rpm-config-9.1.0-15.fc16 has been gone 
through updates), if you're using a %configure-style spec file, defining 
the magic macro is all you have to do.  The rpm macros will notice the 
macro, and put the right magic into CFLAGS and LDFLAGS, and everything 
is great and wonderful.

If you're _not_ using %configure, then you have to do whatever is 
conventional for your build system to get CFLAGS and LDFLAGS inherited 
properly.   For CFLAGS, this will be $RPM_OPT_FLAGS or %{optflags} as 
before.  As of rpm-4.9.1-3.fc16, you will be able to say $RPM_LD_FLAGS 
for the corresponding LDFLAGS values.  Until then, there is no such 
shell variable, but you can get the same effect from 
%{?__global_ldflags}.  Yes, that's ugly, sorry.

If you are the owner of one of the packages listed here:

https://fedoraproject.org/wiki/User:Kevin/DRAFT_When_to_use_PIE_compiler_flags

Then I have locally built (though not extensively tested) your package 
with the appropriate specfile modifications, and the results do indeed 
appear to be fully hardened.  If you would like to handle the rebuilds 
yourself, please let me know.  Otherwise I will submit them myself once 
the relevant updates have gone through.

If you've made it to the end, congratulations.  Please let me know if 
there are any issues, or any questions I can answer.  In particular if 
the performance impact of these flags is excessive for you, there are 
some ways it can be mitigated that are out of scope for this particular 
email.

- ajax


More information about the devel-announce mailing list