Beginning with Ubuntu 19.10
, it’s packaged GCC now has some additional hardening options enabled by default (in addition to existing defaults like -fstack-protector-strong
and reducing the minimum ssp buffer size). The new additions are-fcf-protection=full
and -fstack-clash-protection
.
-fcf-protection=[full|branch|return|none] Enable code instrumentation of control-flow transfers to increase program security by checking that target addresses of control-flow transfer instructions (such as indirect function call, function return, indirect jump) are valid. This prevents diverting the flow of control to an unexpected target. This is intended to protect against such threats as Return-oriented Programming (ROP), and similarly call/jmp-oriented programming (COP/JOP).
-fstack-clash-protection Generate code to prevent stack clash style attacks. When this option is enabled, the compiler will only allocate one page of stack space at a time and each page is accessed immediately after allocation. Thus, it prevents allocations from jumping over any stack guard page provided by the operating system.
If your interested you can grab gcc-9_9.3.0-10ubuntu2.debian.tar.xz
from https://packages.ubuntu.com/focal/g++-9. The relevant changes are part of the gcc-distro-specs
patches, along with the relevant additions to the gcc manages:
NOTE: In Ubuntu 19.10 and later versions, -fcf-protection is enabled by default for C, C++, ObjC, ObjC++, if none of -fno-cf-protection nor -fcf-protection=* are found.
NOTE: In Ubuntu 19.10 and later versions, -fstack-clash-protection is enabled by default for C, C++, ObjC, ObjC++, unless -fno-stack-clash-protection is found.
So, if you’re C++ using GCC on Ubuntu 19.10 or later, these options will be active unless you explicitly opt out. This can be observed with a small test:
0int main() { return 0; }
0g++ --version
1g++ (Ubuntu 9.3.0-10ubuntu2) 9.3.0
2
3g++ test.cpp
4
5objdump -dC a.out
6..
70000000000001129 <main>:
8 1129: f3 0f 1e fa endbr64
9 112d: 55 push %rbp
10 112e: 48 89 e5 mov %rsp,%rbp
11 1131: b8 00 00 00 00 mov $0x0,%eax
12 1136: 5d pop %rbp
13 1137: c3 retq
14 1138: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
15 113f: 00
16
17
18# recompile opting out of control flow protection
19g++ test.cpp -fcf-protection=none
20
21objdump -dC a.out
22...
230000000000001129 <main>:
24 1129: 55 push %rbp
25 112a: 48 89 e5 mov %rsp,%rbp
26 112d: b8 00 00 00 00 mov $0x0,%eax
27 1132: 5d pop %rbp
28 1133: c3 retq
29 1134: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
30 113b: 00 00 00
31 113e: 66 90 xchg %ax,%ax
Note the insertion of an endbr64
instruction when compiling and not opting out. This instruction is part of the Intel Control-flow Enforcement Technology spec, which the GCC control flow implementation is based on.
If we’re still doing gitian builds for the 0.21.0
and 0.22.0
releases, we’d likely update the gitian image to Ubuntu Focal, which would mean that the GCC used for gitian builds would also be using these options by default. So we should decide whether we want to explicitly turn these options on as part of our hardening options (although not just for this reason), or, we should be opting-out.
GCC has supported both options since 8.0.0. Clang has supported -fcf-protection
from 7.0.0 and will support -fstack-clash-protection
in it’s upcoming 11.0.0 release.