diff --git a/man/dpkg-buildflags.pod b/man/dpkg-buildflags.pod index dac1be7ad..56372b277 100644 --- a/man/dpkg-buildflags.pod +++ b/man/dpkg-buildflags.pod @@ -457,6 +457,26 @@ Disabling B will also disable this setting. This feature has the same requirements as B, and in addition also requires gcc 4.9 and later. +=item B + +This setting adds +B<-fstack-clash-protection> +to B, B, B and B. This adds +extra instructions around variable length stack memory allocations (via +B or variable length arrays) to probe each page of memory at +allocation time. This mitigates stack-clash attacks by ensuring all stack +memory allocations are valid (or by raising a segmentation fault if they +are not, and hence turning a possible code-execution attack into a denial +of service). + +=item B + +This setting adds +B<-fcf-protection> +to B, B, B and B. This instructs +the compiler to generate additional instructions to support Intel's +Control-flow Enforcement Technology (CET). + =item B This setting (enabled by default) adds diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm index eb06149af..4b2fdd302 100644 --- a/scripts/Dpkg/Vendor/Debian.pm +++ b/scripts/Dpkg/Vendor/Debian.pm @@ -124,6 +124,8 @@ sub _add_build_flags { format => 1, relro => 1, bindnow => 0, + stackclashprotection => 0, + cfprotection => 0, }, ); @@ -350,6 +352,16 @@ sub _add_build_flags { # relro not implemented on ia64, hppa, avr32. $use_feature{hardening}{relro} = 0; } + if ($cpu !~ /^(?:amd64|arm64|i386|ppc64|ppc64el|s390x|x32)$/) { + # Stack clash protection only enabled on amd64, arm64, i386, ppc64, + # ppc64el, s390x, x32 + $use_feature{hardening}{stackclashprotection} = 0; + } + if ($cpu !~ /^(?:amd64|i386|x32)$/) { + # Control flow protection is Intel only so only enabled on amd64, + # i386, and x32 + $use_feature{hardening}{cfprotection} = 0; + } # Mask features that might be influenced by other flags. if ($opts_build->has('noopt')) { @@ -440,6 +452,24 @@ sub _add_build_flags { $flags->append('LDFLAGS', '-Wl,-z,now'); } + # Stack clash protection + if ($use_feature{hardening}{stackclashprotection}) { + my $flag = '-fstack-clash-protection'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('OBJCFLAGS', $flag); + $flags->append('OBJCXXFLAGS', $flag); + } + + # Control flow integrity protection + if ($use_feature{hardening}{cfprotection}) { + my $flag = '-fcf-protection'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('OBJCFLAGS', $flag); + $flags->append('OBJCXXFLAGS', $flag); + } + ## Commit # Set used features to their builtin setting if unset. diff --git a/scripts/Dpkg/Vendor/Ubuntu.pm b/scripts/Dpkg/Vendor/Ubuntu.pm index 0352a127d..a491e868d 100644 --- a/scripts/Dpkg/Vendor/Ubuntu.pm +++ b/scripts/Dpkg/Vendor/Ubuntu.pm @@ -100,12 +100,50 @@ sub run_hook { require Dpkg::BuildOptions; - my $build_opts = Dpkg::BuildOptions->new(); + my $opts_build = Dpkg::BuildOptions->new(envvar => 'DEB_BUILD_OPTIONS'); + my $opts_maint = Dpkg::BuildOptions->new(envvar => 'DEB_BUILD_MAINT_OPTIONS'); + + # in Ubuntu we enable stackclashprotection and cfprotection by + # default - so if these have not been disabled then we need to + # enable them - and if they have been disabled we need to emit + # flags to override and disable them - and ensure + # DEB_BUILD_MAINT_OPTIONS takes precedence over DEB_BUILD_OPTIONS + my $hardening = ''; + if ($opts_maint->has('hardening')) { + $hardening = $opts_maint->get('hardening'); + } elsif ($opts_build->has('hardening')) { + $hardening = $opts_build->get('hardening'); + } + + require Dpkg::Arch; - if (!$build_opts->has('noopt')) { - require Dpkg::Arch; + my $arch = Dpkg::Arch::get_host_arch(); + my ($abi, $libc, $os, $cpu) = Dpkg::Arch::debarch_to_debtuple($arch); + + if ($cpu =~ /^(?:amd64|arm64|i386|ppc64|ppc64el|s390x|x32)$/) { + # Stack clash protection only enabled on amd64, arm64, i386, ppc64, + # ppc64el, s390x, x32 + my $disabled = $hardening =~ "-stackclashprotection"; + my $flag = $disabled ? '-fno-stack-clash-protection' : '-fstack-clash-protection'; + for my $envvar (qw(CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS)) { + $flags->append($envvar, $flag); + } + $flags->set_feature('hardening', 'stackclashprotection', !$disabled); + } + + + if ($cpu =~ /^(?:amd64|i386|x32)$/) { + # Control flow protection is Intel only so only enabled on amd64, + # i386, and x32 + my $disabled = $hardening =~ "-cfprotection"; + my $flag = $disabled ? '-fcf-protection=none' : '-fcf-protection'; + for my $envvar (qw(CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS)) { + $flags->append($envvar, $flag); + } + $flags->set_feature('hardening', 'cfprotection', !$disabled); + } - my $arch = Dpkg::Arch::get_host_arch(); + if (!$opts_build->has('noopt')) { if (Dpkg::Arch::debarch_eq($arch, 'ppc64el')) { for my $flag (qw(CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS GCJFLAGS FFLAGS FCFLAGS)) { diff --git a/scripts/t/Dpkg_BuildFlags.t b/scripts/t/Dpkg_BuildFlags.t index b087e0ab2..37ac5fcd1 100644 --- a/scripts/t/Dpkg_BuildFlags.t +++ b/scripts/t/Dpkg_BuildFlags.t @@ -53,10 +53,12 @@ my %known_features = ( ) ], hardening => [ qw( bindnow + cfprotection format fortify pie relro + stackclashprotection stackprotector stackprotectorstrong ) ],