diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index fae1394a7f..a7e4b80df9 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -158,6 +158,12 @@ def check_MACHO_NOUNDEFS(binary) -> bool: ''' return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS) +def check_MACHO_FIXUP_CHAINS(binary) -> bool: + ''' + Check for use of chained fixups. + ''' + return binary.has_dyld_chained_fixups + def check_MACHO_Canary(binary) -> bool: ''' Check for use of stack canary @@ -208,6 +214,7 @@ BASE_PE = [ BASE_MACHO = [ ('NOUNDEFS', check_MACHO_NOUNDEFS), ('Canary', check_MACHO_Canary), + ('FIXUP_CHAINS', check_MACHO_FIXUP_CHAINS), ] CHECKS = { diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index 9da76f6c1c..780e5a7dd3 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -119,27 +119,31 @@ class TestSecurityChecks(unittest.TestCase): arch = get_arch(cc, source, executable) if arch == lief.ARCHITECTURES.X86: - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector', '-Wl,-no_fixup_chains']), + (1, executable+': failed NOUNDEFS Canary FIXUP_CHAINS PIE NX CONTROL_FLOW')) + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector', '-Wl,-fixup_chains']), (1, executable+': failed NOUNDEFS Canary PIE NX CONTROL_FLOW')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fstack-protector-all']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fstack-protector-all', '-Wl,-fixup_chains']), (1, executable+': failed NOUNDEFS PIE NX CONTROL_FLOW')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-fstack-protector-all']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-fstack-protector-all', '-Wl,-fixup_chains']), (1, executable+': failed NOUNDEFS PIE CONTROL_FLOW')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-fstack-protector-all']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-fstack-protector-all', '-Wl,-fixup_chains']), (1, executable+': failed PIE CONTROL_FLOW')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-bind_at_load','-fstack-protector-all']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-bind_at_load','-fstack-protector-all', '-Wl,-fixup_chains']), (1, executable+': failed PIE CONTROL_FLOW')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-bind_at_load','-fstack-protector-all', '-fcf-protection=full']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-bind_at_load','-fstack-protector-all', '-fcf-protection=full', '-Wl,-fixup_chains']), (1, executable+': failed PIE')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie','-Wl,-bind_at_load','-fstack-protector-all', '-fcf-protection=full']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie','-Wl,-bind_at_load','-fstack-protector-all', '-fcf-protection=full', '-Wl,-fixup_chains']), (0, '')) else: # arm64 darwin doesn't support non-PIE binaries, control flow or executable stacks - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-no_fixup_chains']), + (1, executable+': failed NOUNDEFS Canary FIXUP_CHAINS')) + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-fixup_chains']), (1, executable+': failed NOUNDEFS Canary')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fstack-protector-all']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fstack-protector-all', '-Wl,-fixup_chains']), (1, executable+': failed NOUNDEFS')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-bind_at_load','-fstack-protector-all']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-bind_at_load','-fstack-protector-all', '-Wl,-fixup_chains']), (0, '')) diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk index 03e9002ecd..4860934a8e 100644 --- a/depends/packages/native_cctools.mk +++ b/depends/packages/native_cctools.mk @@ -5,6 +5,7 @@ $(package)_file_name=$($(package)_version).tar.gz $(package)_sha256_hash=6b73269efdf5c58a070e7357b66ee760501388549d6a12b423723f45888b074b $(package)_build_subdir=cctools $(package)_dependencies=native_libtapi +$(package)_patches=no_fixup_chains.patch define $(package)_set_vars $(package)_config_opts=--target=$(host) --enable-lto-support @@ -18,11 +19,13 @@ ifneq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) define $(package)_preprocess_cmds mkdir -p $($(package)_staging_prefix_dir)/lib && \ cp $(llvm_lib_dir)/libLTO.so $($(package)_staging_prefix_dir)/lib/ && \ - cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub cctools + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub cctools && \ + patch -p1 < $($(package)_patch_dir)/no_fixup_chains.patch endef else define $(package)_preprocess_cmds - cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub cctools + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub cctools && \ + patch -p1 < $($(package)_patch_dir)/no_fixup_chains.patch endef endif diff --git a/depends/patches/native_cctools/no_fixup_chains.patch b/depends/patches/native_cctools/no_fixup_chains.patch new file mode 100644 index 0000000000..2516ea8200 --- /dev/null +++ b/depends/patches/native_cctools/no_fixup_chains.patch @@ -0,0 +1,23 @@ +commit 5860b35ff6c7241d1c35a1b3197b45e5c9ff86cf +Author: fanquake +Date: Thu Jun 29 11:52:43 2023 +0100 + + ld64: add support for -no_fixup_chains + + This is added in later versions, and is required if we want to be able + to disable fixup_chains, for use in security tests. + +diff --git a/cctools/ld64/src/ld/Options.cpp b/cctools/ld64/src/ld/Options.cpp +index 15e8e88..b6580af 100644 +--- a/cctools/ld64/src/ld/Options.cpp ++++ b/cctools/ld64/src/ld/Options.cpp +@@ -4128,6 +4128,9 @@ void Options::parse(int argc, const char* argv[]) + else if ( strcmp(arg, "-fixup_chains") == 0 ) { + fMakeChainedFixups = true; + } ++ else if ( strcmp(arg, "-no_fixup_chains") == 0 ) { ++ fMakeChainedFixups = false; ++ } + else if (strcmp(arg, "-debug_variant") == 0) { + fDebugVariant = true; + }