Skip to content

Commit ac21090

Browse files
committed
Merge #18629: scripts: add PE .reloc section check to security-check.py
3e38023 scripts: add PE .reloc section check to security-check.py (fanquake) Pull request description: The `ld` in binutils has historically had a few issues with PE binaries, there's a good summary in this [thread](https://sourceware.org/bugzilla/show_bug.cgi?id=19011). One issue in particular was `ld` stripping the `.reloc` section out of PE binaries, even though it's required for functioning ASLR. This was [reported by a Tor developer in 2014](https://sourceware.org/bugzilla/show_bug.cgi?id=17321) and they have been patching their [own binutils](https://gitweb.torproject.org/builders/tor-browser-build.git/tree/projects/binutils) ever since. However their patch only made it into binutils at the [start of this year](https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=dc9bd8c92af67947db44b3cb428c050259b15cd0). It adds an `--enable-reloc-section` flag, which is turned on by default if you are using `--dynamic-base`. In the mean time this issue has also been worked around by other projects, such as FFmpeg, see [this commit](TheRyuu/FFmpeg@91b668a). I have checked our recent supported Windows release binaries, and they do contain a `.reloc` section. From what I understand, we are using all the right compile/linker flags, including `-pie` & `-fPIE`, and have never run into the crashing/entrypoint issues that other projects might have seen. One other thing worth noting here, it how Debian/Ubuntu patch the binutils that they distribute, because that's what we end up using in our gitian builds. In the binutils-mingw-w64 in Bionic (18.04), which we currently use in gitian, PE hardening options/security flags are enabled by default. See the [changelog](https://changelogs.ubuntu.com/changelogs/pool/universe/b/binutils-mingw-w64/binutils-mingw-w64_8ubuntu1/changelog) and the [relevant commit](https://salsa.debian.org/mingw-w64-team/binutils-mingw-w64/-/commit/452b3013b8280cbe35eaeb166a43621b88d5f8b7). However in Focal (20.04), this has now been reversed. PE hardening options are no-longer the default. See the [changelog](https://changelogs.ubuntu.com/changelogs/pool/universe/b/binutils-mingw-w64/binutils-mingw-w64_8.8/changelog) and [relevant commit](https://salsa.debian.org/mingw-w64-team/binutils-mingw-w64/-/commit/7bd8b2fbc242a8c2fc2217f29fd61f94d3babf6f), which cites same .reloc issue mentioned here. Given that we explicitly specify/opt-in to everything that we want to use, the defaults aren't necessarily an issue for us. However I think it highlights the importance of continuing to be explicit about what we want, and not falling-back or relying on upstream. This was also prompted by the possibility of us doing link time garbage collection, see #18579 & #18605. It seemed some sanity checks would be worthwhile in-case the linker goes haywire while garbage collecting. I think Guix is going to bring great benefits when dealing with these kinds of issues. Carl you might have something to say in that regard. ACKs for top commit: dongcarl: ACK 3e38023 Tree-SHA512: af14d63bdb334bde548dd7de3e0946556b7e2598d817b56eb4e75b3f56c705c26aa85dd9783134c4b6a7aeb7cb4de567eed996e94d533d31511f57ed332287da
2 parents 5352d14 + 3e38023 commit ac21090

File tree

2 files changed

+22
-8
lines changed

2 files changed

+22
-8
lines changed

contrib/devtools/security-check.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,17 @@ def check_PE_HIGH_ENTROPY_VA(executable):
158158
reqbits = 0
159159
return (bits & reqbits) == reqbits
160160

161+
def check_PE_RELOC_SECTION(executable) -> bool:
162+
'''Check for a reloc section. This is required for functional ASLR.'''
163+
p = subprocess.Popen([OBJDUMP_CMD, '-h', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
164+
(stdout, stderr) = p.communicate()
165+
if p.returncode:
166+
raise IOError('Error opening file')
167+
for line in stdout.splitlines():
168+
if '.reloc' in line:
169+
return True
170+
return False
171+
161172
def check_PE_NX(executable):
162173
'''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)'''
163174
(arch,bits) = get_PE_dll_characteristics(executable)
@@ -247,7 +258,8 @@ def check_MACHO_Canary(executable) -> bool:
247258
'PE': [
248259
('DYNAMIC_BASE', check_PE_DYNAMIC_BASE),
249260
('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA),
250-
('NX', check_PE_NX)
261+
('NX', check_PE_NX),
262+
('RELOC_SECTION', check_PE_RELOC_SECTION)
251263
],
252264
'MACHO': [
253265
('PIE', check_MACHO_PIE),

contrib/devtools/test-security-check.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,15 @@ def test_PE(self):
4949
cc = 'x86_64-w64-mingw32-gcc'
5050
write_testcode(source)
5151

52-
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va']),
53-
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA NX'))
54-
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va']),
55-
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA'))
56-
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--no-high-entropy-va']),
57-
(1, executable+': failed HIGH_ENTROPY_VA'))
58-
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va']),
52+
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
53+
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA NX RELOC_SECTION'))
54+
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
55+
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA RELOC_SECTION'))
56+
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
57+
(1, executable+': failed HIGH_ENTROPY_VA RELOC_SECTION'))
58+
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va','-no-pie','-fno-PIE']),
59+
(1, executable+': failed RELOC_SECTION'))
60+
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE']),
5961
(0, ''))
6062

6163
def test_MACHO(self):

0 commit comments

Comments
 (0)