/x;
# Regex to catch (GCC) compiler warnings.
my $warning_regex = qr/^(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]$/;
+# Regex to catch libtool commands and not lines which show commands executed
+# by libtool (e.g. libtool: link: ...).
+my $libtool_regex = qr/\blibtool\s.*--mode=/;
# List of source file extensions which require preprocessing.
my @source_preprocess_compile_cpp = (
# real regexps below for better execution speed).
my @def_cflags = (
'-g',
- '-O(?:2|3)',
+ '-O(?:2|3)', # keep at index 1, search for @def_cflags_debug to change it
+);
+my @def_cflags_debug = (
+ # These flags indicate a debug build which disables checks for -O2.
+ '-O0',
+ '-Og',
);
my @def_cflags_format = (
'-Wformat(?:=2)?', # -Wformat=2 implies -Wformat, accept it too
'-fstack-protector',
'--param[= ]ssp-buffer-size=4',
);
+my @def_cflags_stack_strong = (
+ '-fstack-protector-strong',
+);
my @def_cflags_pie = (
'-fPIE',
);
\@def_cflags_format,
\@def_cflags_fortify,
\@def_cflags_stack,
+ \@def_cflags_stack_strong,
\@def_cflags_pie,
\@def_cxxflags,
\@def_cppflags,
# References to all used flags.
my @flag_refs_all = (
@flag_refs,
+ \@def_cflags_debug,
\@def_cppflags_fortify_bad,
\@def_ldflags_pic,
);
sub is_non_verbose_build {
my ($line, $next_line, $skip_ref) = @_;
+ if ($line =~ /$libtool_regex/o) {
+ # libtool's --silent hides the real compiler flags.
+ if ($line =~ /\s--silent/) {
+ return 1;
+ # If --silent is not present, skip this line as some compiler flags
+ # might be missing (e.g. -fPIE) which are handled correctly by libtool
+ # internally. libtool displays the real compiler command on the next
+ # line, so the flags are checked as usual.
+ } else {
+ ${$skip_ref} = 1;
+ return 0;
+ }
+ }
+
if (not (index($line, 'checking if you want to see long compiling messages... no') == 0
or $line =~ /^\s*\[?(?:CC|CCLD|C\+\+|CXX|CXXLD|LD|LINK)\]?\s+(.+?)$/
or $line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/
return 0 if $line =~ /^\s*C\+\+.+?:\s+(?:yes|no)\s*$/;
return 0 if $line =~ /^\s*C\+\+ Library: stdc\+\+$/;
# "Compiling" non binary files.
- return 0 if $line =~ /^\s*Compiling \S+\.(?:py|el)['"]?(?:\.\.\.)?$/;
+ return 0 if $line =~ /^\s*Compiling \S+\.(?:py|el)['"]?\s*(?:\.\.\.)?$/;
# "Compiling" with no file name.
if ($line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/) {
# $file_extension_regex may need spaces around the filename.
return 1;
}
-# Remove @flags from $flag_refs_ref, and $flag_renames_ref.
+# Remove @flags from $flag_refs_ref, uses $flag_renames_ref as reference.
sub remove_flags {
my ($flag_refs_ref, $flag_renames_ref, @flags) = @_;
my $harden_format = 1;
my $harden_fortify = 1;
my $harden_stack = 1;
+ my $harden_stack_strong = 1;
my $harden_relro = 1;
my $harden_bindnow = $option_bindnow; # defaults to 0
my $harden_pie = $option_pie; # defaults to 0
#
# Packages which were built before 1.16.1 but used their own hardening
# flags are not checked.
+ #
+ # Strong stack protector is used since dpkg 1.17.11.
if ($option_buildd
and index($line, 'Toolchain package versions: ') == 0) {
require Dpkg::Version;
- if (not $line =~ /\bdpkg-dev_(\S+)/
- or Dpkg::Version::version_compare($1, '1.16.1') < 0) {
+
+ my $disable = 1;
+ my $disable_strong = 1;
+
+ if ($line =~ /\bdpkg-dev_(\S+)/) {
+ if (Dpkg::Version::version_compare($1, '1.16.1') >= 0) {
+ $disable = 0;
+ }
+ if (Dpkg::Version::version_compare($1, '1.17.11') >= 0) {
+ $disable_strong = 0;
+ }
+ }
+
+ if ($disable) {
$harden_format = 0;
$harden_fortify = 0;
$harden_stack = 0;
$harden_bindnow = 0;
$harden_pie = 0;
}
+ if ($disable_strong) {
+ $harden_stack_strong = 0;
+ }
}
# The following two versions of CMake in Debian obeyed CPPFLAGS, but
my $continuation = 0;
my $complete_line = undef;
+ my $non_verbose;
while (my $line = <$fh>) {
# And stop at the end of the build log. Package details (reported by
# the buildd logs) are not important for us. This also prevents false
last if index($line, 'Build finished at ') == 0
and $line =~ /^Build finished at \d{8}-\d{4}$/;
+ if (not $continuation) {
+ $non_verbose = 0;
+ }
+
# Detect architecture automatically unless overridden.
if (not $arch
and index($line, 'dpkg-buildpackage: host architecture ') == 0) {
}
# Check if this line indicates a non verbose build.
- my $non_verbose = is_non_verbose_build($line);
+ my $skip = 0;
+ $non_verbose |= is_non_verbose_build($line, undef, \$skip);
+ next if $skip;
# One line may contain multiple commands (";"). Treat each one as
# single line. parse_line() is slow, only use it when necessary.
# Option or auto detected.
if ($arch) {
- # The following was partially copied from dpkg-dev 1.17.1
+ # The following was partially copied from dpkg-dev 1.17.11
# (/usr/share/perl5/Dpkg/Vendor/Debian.pm, add_hardening_flags()),
# copyright Raphaƫl Hertzog <hertzog@debian.org>, Kees Cook
# <kees@debian.org>, Canonical, Ltd. licensed under GPL version 2 or
$cpu =~ /^(?:hppa|mips|mipsel|avr32)$/) {
$harden_pie = 0;
}
- if ($cpu =~ /^(?:ia64|alpha|mips|mipsel|hppa|arm64)$/
- or $arch eq 'arm') {
+ if ($cpu =~ /^(?:ia64|alpha|mips|mipsel|hppa)$/ or $arch eq 'arm') {
$harden_stack = 0;
+ $harden_stack_strong = 0;
+ }
+ if ($arch =~ /^(?:m68k|or1k|powerpcspe|sh4|x32)$/) {
+ $harden_stack_strong = 0;
}
if ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
$harden_relro = 0;
@cxxflags = (@cxxflags, @def_cflags_pie);
@ldflags = (@ldflags, @def_ldflags_pie);
}
- if ($harden_stack) {
+ if ($harden_stack_strong) {
+ @cflags = (@cflags, @def_cflags_stack_strong);
+ @cxxflags = (@cxxflags, @def_cflags_stack_strong);
+ } elsif ($harden_stack) {
@cflags = (@cflags, @def_cflags_stack);
@cxxflags = (@cxxflags, @def_cflags_stack);
}
$statistics{link}++ if $link;
}
+ # Check if there are flags indicating a debug build. If that's true,
+ # skip the check for -O2. This prevents fortification, but that's fine
+ # for a debug build.
+ if (any_flags_used($line, @def_cflags_debug)) {
+ remove_flags([\@cflags], \%flag_renames, $def_cflags[1]);
+ }
+
# Check hardening flags.
my @missing;
if ($compile and not all_flags_used($line, \@missing, @cflags)