diff options
author | merge-script <fanquake@gmail.com> | 2024-09-12 11:32:31 +0100 |
---|---|---|
committer | merge-script <fanquake@gmail.com> | 2024-09-12 11:32:31 +0100 |
commit | fcb61bbc8dd7d975330d35f9d7a9a0cc823928de (patch) | |
tree | 3f86544fbde687c4340e737a1ea1bfb84fe69c65 | |
parent | 85833cf05fb62fdcd41df8bfd4df33d62aae87d0 (diff) | |
parent | be4f78275fa6608b11377dd5a29a809597d3fe8d (diff) | |
download | bitcoin-fcb61bbc8dd7d975330d35f9d7a9a0cc823928de.tar.xz |
Merge bitcoin/bitcoin#27038: security-check: test for `_FORTIFY_SOURCE` usage in release binaries
be4f78275fa6608b11377dd5a29a809597d3fe8d contrib: test for FORTIFY_SOURCE in security-check.py (fanquake)
Pull request description:
Test for the existence of fortified functions in the ELF release binaries.
Currently skips `bitcoin-util` and checks for RISC-V.
ACKs for top commit:
TheCharlatan:
ACK be4f78275fa6608b11377dd5a29a809597d3fe8d
Tree-SHA512: decea5f359f1e673aa0119916f674f409a13b69db7da366cd95c1540201e117ff5a979da67bc2517fe786c2ac23d1006a9aaf662d7eadeec35da6aae4998c065
-rwxr-xr-x | contrib/devtools/security-check.py | 30 | ||||
-rwxr-xr-x | contrib/devtools/test-security-check.py | 11 |
2 files changed, 33 insertions, 8 deletions
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 46f9ee915f..4c20685b51 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -7,6 +7,7 @@ Perform basic security checks on a series of executables. Exit status will be 0 if successful, and the program will be silent. Otherwise the exit status will be 1 and it will log which executables failed which checks. ''' +import re import sys import lief @@ -116,6 +117,25 @@ def check_ELF_CONTROL_FLOW(binary) -> bool: return True return False +def check_ELF_FORTIFY(binary) -> bool: + + # bitcoin-util does not currently contain any fortified functions + if 'Bitcoin Core bitcoin-util utility version ' in binary.strings: + return True + + chk_funcs = set() + + for sym in binary.imported_symbols: + match = re.search(r'__[a-z]*_chk', sym.name) + if match: + chk_funcs.add(match.group(0)) + + # ignore stack-protector and bdb + chk_funcs.discard('__stack_chk') + chk_funcs.discard('__db_chk') + + return len(chk_funcs) >= 1 + def check_PE_DYNAMIC_BASE(binary) -> bool: '''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)''' return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists @@ -228,11 +248,11 @@ BASE_MACHO = [ CHECKS = { lief.EXE_FORMATS.ELF: { - lief.ARCHITECTURES.X86: BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW)], - lief.ARCHITECTURES.ARM: BASE_ELF, - lief.ARCHITECTURES.ARM64: BASE_ELF, - lief.ARCHITECTURES.PPC: BASE_ELF, - lief.ARCHITECTURES.RISCV: BASE_ELF, + lief.ARCHITECTURES.X86: BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW), ('FORTIFY', check_ELF_FORTIFY)], + lief.ARCHITECTURES.ARM: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], + lief.ARCHITECTURES.ARM64: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], + lief.ARCHITECTURES.PPC: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], + lief.ARCHITECTURES.RISCV: BASE_ELF, # Skip FORTIFY. See https://github.com/lief-project/LIEF/issues/1082. }, lief.EXE_FORMATS.PE: { lief.ARCHITECTURES.X86: BASE_PE, diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index 4bec6bfe7c..99f171608d 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -59,17 +59,22 @@ class TestSecurityChecks(unittest.TestCase): arch = get_arch(cxx, source, executable) if arch == lief.ARCHITECTURES.X86: - pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code', '-fcf-protection=full'] + pass_flags = ['-D_FORTIFY_SOURCE=3', '-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code', '-fcf-protection=full'] self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-zexecstack']), (1, executable + ': failed NX')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-no-pie','-fno-PIE']), (1, executable + ': failed PIE')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-znorelro']), (1, executable + ': failed RELRO')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-z,noseparate-code']), (1, executable + ': failed SEPARATE_CODE')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fcf-protection=none']), (1, executable + ': failed CONTROL_FLOW')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-U_FORTIFY_SOURCE']), (1, executable + ': failed FORTIFY')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, '')) else: - pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code'] + pass_flags = ['-D_FORTIFY_SOURCE=3', '-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code'] self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-zexecstack']), (1, executable + ': failed NX')) - self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-no-pie','-fno-PIE']), (1, executable + ': failed PIE')) + # LIEF fails to parse RISC-V with no PIE correctly, and doesn't detect the fortified function, + # so skip this test for RISC-V (for now). See https://github.com/lief-project/LIEF/issues/1082. + if arch != lief.ARCHITECTURES.RISCV: + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-no-pie','-fno-PIE']), (1, executable + ': failed PIE')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-U_FORTIFY_SOURCE']), (1, executable + ': failed FORTIFY')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-znorelro']), (1, executable + ': failed RELRO')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-z,noseparate-code']), (1, executable + ': failed SEPARATE_CODE')) self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, '')) |