2018-04-16 22:03:08 +02:00
|
|
|
#!/usr/bin/env python3
|
2016-09-19 17:02:23 +02:00
|
|
|
# Copyright (c) 2015-2016 The Bitcoin Core developers
|
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2015-10-19 14:53:56 +02:00
|
|
|
'''
|
|
|
|
Test script for security-check.py
|
|
|
|
'''
|
|
|
|
import subprocess
|
|
|
|
import unittest
|
|
|
|
|
|
|
|
def write_testcode(filename):
|
2018-06-16 15:21:01 +02:00
|
|
|
with open(filename, 'w', encoding="utf8") as f:
|
2015-10-19 14:53:56 +02:00
|
|
|
f.write('''
|
|
|
|
#include <stdio.h>
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
printf("the quick brown fox jumps over the lazy god\\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
''')
|
|
|
|
|
|
|
|
def call_security_check(cc, source, executable, options):
|
|
|
|
subprocess.check_call([cc,source,'-o',executable] + options)
|
2018-04-16 22:03:08 +02:00
|
|
|
p = subprocess.Popen(['./security-check.py',executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
2015-10-19 14:53:56 +02:00
|
|
|
(stdout, stderr) = p.communicate()
|
|
|
|
return (p.returncode, stdout.rstrip())
|
|
|
|
|
|
|
|
class TestSecurityChecks(unittest.TestCase):
|
|
|
|
def test_ELF(self):
|
|
|
|
source = 'test1.c'
|
|
|
|
executable = 'test1'
|
|
|
|
cc = 'gcc'
|
|
|
|
write_testcode(source)
|
|
|
|
|
2018-07-30 16:01:58 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE']),
|
2015-10-19 14:53:56 +02:00
|
|
|
(1, executable+': failed PIE NX RELRO Canary'))
|
2018-07-30 16:01:58 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE']),
|
2015-10-19 14:53:56 +02:00
|
|
|
(1, executable+': failed PIE RELRO Canary'))
|
2018-07-30 16:01:58 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-no-pie','-fno-PIE']),
|
2015-10-19 14:53:56 +02:00
|
|
|
(1, executable+': failed PIE RELRO'))
|
2018-07-30 16:01:58 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']),
|
2015-10-19 14:53:56 +02:00
|
|
|
(1, executable+': failed RELRO'))
|
2018-07-30 16:01:58 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']),
|
2015-10-19 14:53:56 +02:00
|
|
|
(0, ''))
|
|
|
|
|
2020-03-28 11:48:19 +01:00
|
|
|
def test_PE(self):
|
2018-05-07 15:14:33 +02:00
|
|
|
source = 'test1.c'
|
|
|
|
executable = 'test1.exe'
|
|
|
|
cc = 'x86_64-w64-mingw32-gcc'
|
|
|
|
write_testcode(source)
|
|
|
|
|
Merge #18629: scripts: add PE .reloc section check to security-check.py
3e38023af724a76972d39cbccfb0bba4c54a0323 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](https://github.com/TheRyuu/FFmpeg/commit/91b668acd6decec0a6f8d20bf56e2644f96adcb9).
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 3e38023af724a76972d39cbccfb0bba4c54a0323
Tree-SHA512: af14d63bdb334bde548dd7de3e0946556b7e2598d817b56eb4e75b3f56c705c26aa85dd9783134c4b6a7aeb7cb4de567eed996e94d533d31511f57ed332287da
2020-04-28 07:08:19 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
|
|
|
|
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA NX RELOC_SECTION'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
|
|
|
|
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA RELOC_SECTION'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
|
|
|
|
(1, executable+': failed HIGH_ENTROPY_VA RELOC_SECTION'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va','-no-pie','-fno-PIE']),
|
|
|
|
(1, executable+': failed RELOC_SECTION'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE']),
|
2020-03-28 11:48:19 +01:00
|
|
|
(0, ''))
|
|
|
|
|
|
|
|
def test_MACHO(self):
|
|
|
|
source = 'test1.c'
|
|
|
|
executable = 'test1'
|
|
|
|
cc = 'clang'
|
|
|
|
write_testcode(source)
|
|
|
|
|
2020-04-22 10:19:51 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector']),
|
|
|
|
(1, executable+': failed PIE NOUNDEFS NX LAZY_BINDINGS Canary'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fstack-protector-all']),
|
|
|
|
(1, executable+': failed PIE NOUNDEFS NX LAZY_BINDINGS'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-fstack-protector-all']),
|
|
|
|
(1, executable+': failed PIE NOUNDEFS LAZY_BINDINGS'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-fstack-protector-all']),
|
|
|
|
(1, executable+': failed PIE LAZY_BINDINGS'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-bind_at_load','-fstack-protector-all']),
|
2020-03-28 11:48:19 +01:00
|
|
|
(1, executable+': failed PIE'))
|
2020-04-22 10:19:51 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie','-Wl,-bind_at_load','-fstack-protector-all']),
|
2020-03-28 11:48:19 +01:00
|
|
|
(0, ''))
|
2015-10-19 14:53:56 +02:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|
|
|
|
|