Default compiler and linker flags¶
Built as PIE¶
Programs built as Position Independent Executables (PIE) with -fPIE -pie
take advantage of exec ASLR. This protects against “return-to-text” attacks and
generally frustrates memory corruption attacks. This required centralized
changes to the compiler options when building the entire archive.
PIE incurs a large (5-10%) performance penalty on architectures with small
numbers of general registers (such as x86), so we initially used it only for a
select number of security-critical packages. Some upstreams natively support
building with PIE, while others require the use of hardening-wrapper to
force the correct compiler and linker flags.
PIE on 64-bit architectures doesn’t have the same penalties. We made it the default on amd64, ppc64el, and s390x as of Ubuntu 16.10 (Yakkety Yak). As of Ubuntu 17.10 (Artful Aardvark), we decided that the security benefits are significant enough to enable PIE across all architectures in the Ubuntu archive by default.
See test-built-binaries.py for regression tests.
Built with BIND_NOW¶
This marks ELF programs to resolve all dynamic symbols at start-up (instead of on-demand, also known as “immediate binding”). This allows the Global Offset Table (GOT) to be made entirely read-only (when combined with RELRO).
See test-built-binaries.py for regression tests.
Built with -fcf-protection¶
This instructs the compiler to generate instructions to support Intel’s Control-flow Enforcement Technology (CET).
See test-built-binaries.py for regression tests.
Built with Fortify Source¶
Building programs with -D_FORTIFY_SOURCE=2 (and -O1 or higher) enables
several compile-time and run-time protections in glibc:
Expands unbounded calls to
sprintfandstrcpyinto their “n” length-limited cousins when the size of a destination buffer is known (protects against memory overflows).Stops format string
%nattacks when the format string is in a writable memory segment.Requires checking various important function return codes and arguments (for example,
system,write,open).Requires an explicit file mask when creating new files.
See test-gcc-security.py for regression tests.
Built with -fstack-clash-protection¶
This adds extra instructions around variable-length stack memory allocations
(via alloca() or GCC variable-length arrays) to probe each page of memory
at allocation time. This mitigates stack-clash attacks by ensuring all stack
memory allocations are valid. If they aren’t, it raises a segmentation fault,
turning a possible code-execution attack into a denial of service.
See test-built-binaries.py for regression tests.
Built with RELRO¶
This hardens ELF programs against loader memory area overwrites. The loader marks any areas of the relocation table as read-only for any symbols resolved at load-time (“read-only relocations”). This reduces the area of possible GOT-overwrite-style memory corruption attacks.
See test-gcc-security.py for regression tests.
Stack protector¶
GCC’s -fstack-protector provides a randomized stack canary that protects
against stack overflows and reduces the chances of arbitrary code execution via
controlling return address destinations. This is enabled at compile-time. (A
small number of applications don’t play well with it and have it disabled.) The
routines used for stack checking are part of glibc, but GCC is patched to
enable linking against those routines by default.
See test-gcc-security.py for regression tests.