]> ruderich.org/simon Gitweb - blhc/blhc.git/blobdiff - bin/blhc
update copyright years
[blhc/blhc.git] / bin / blhc
index 2bf99cc6145e6ca40459a253fab32103ba9afd0f..d2a8771515e5e01d8a3890db542cb7cf08244f21 100755 (executable)
--- a/bin/blhc
+++ b/bin/blhc
@@ -2,7 +2,7 @@
 
 # Build log hardening check, checks build logs for missing hardening flags.
 
-# Copyright (C) 2012-2013  Simon Ruderich
+# Copyright (C) 2012-2015  Simon Ruderich
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@ use warnings;
 use Getopt::Long ();
 use Text::ParseWords ();
 
-our $VERSION = '0.04';
+our $VERSION = '0.05';
 
 
 # CONSTANTS/VARIABLES
@@ -48,6 +48,9 @@ my $cc_regex_normal = qr/
     /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 = (
@@ -180,7 +183,12 @@ my $file_extension_regex = qr/
 # 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
@@ -193,6 +201,9 @@ my @def_cflags_stack = (
     '-fstack-protector',
     '--param[= ]ssp-buffer-size=4',
 );
+my @def_cflags_stack_strong = (
+    '-fstack-protector-strong',
+);
 my @def_cflags_pie = (
     '-fPIE',
 );
@@ -203,7 +214,8 @@ my @def_cxxflags = (
 my @def_cppflags = ();
 my @def_cppflags_fortify = (
     '-D_FORTIFY_SOURCE=2', # must be first, see cppflags_fortify_broken()
-    # If you add another flag fix hack below (search for "Hack to fix").
+    # If you add another flag fix hack below (search for "Hack to fix") and
+    # $def_cppflags_fortify[0].
 );
 my @def_cppflags_fortify_bad = (
     # These flags may overwrite -D_FORTIFY_SOURCE=2.
@@ -233,6 +245,7 @@ my @flag_refs = (
     \@def_cflags_format,
     \@def_cflags_fortify,
     \@def_cflags_stack,
+    \@def_cflags_stack_strong,
     \@def_cflags_pie,
     \@def_cxxflags,
     \@def_cppflags,
@@ -245,6 +258,7 @@ my @flag_refs = (
 # References to all used flags.
 my @flag_refs_all = (
     @flag_refs,
+    \@def_cflags_debug,
     \@def_cppflags_fortify_bad,
     \@def_ldflags_pic,
 );
@@ -429,6 +443,20 @@ sub pic_pie_conflict {
 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+(.+?)(?:\.\.\.)?$/
@@ -443,7 +471,7 @@ sub is_non_verbose_build {
     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.
@@ -478,7 +506,7 @@ sub is_non_verbose_build {
     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) = @_;
 
@@ -573,7 +601,7 @@ if ($option_help) {
 }
 if ($option_version) {
     print <<"EOF";
-blhc $VERSION  Copyright (C) 2012-2013  Simon Ruderich
+blhc $VERSION  Copyright (C) 2012-2015  Simon Ruderich
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -676,6 +704,7 @@ foreach my $file (@ARGV) {
     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
@@ -705,11 +734,25 @@ foreach my $file (@ARGV) {
         #
         # 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;
@@ -717,6 +760,9 @@ foreach my $file (@ARGV) {
                 $harden_bindnow = 0;
                 $harden_pie     = 0;
             }
+            if ($disable_strong) {
+                $harden_stack_strong = 0;
+            }
         }
 
         # The following two versions of CMake in Debian obeyed CPPFLAGS, but
@@ -775,6 +821,7 @@ foreach my $file (@ARGV) {
 
     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
@@ -782,6 +829,10 @@ foreach my $file (@ARGV) {
         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) {
@@ -811,7 +862,9 @@ foreach my $file (@ARGV) {
         }
 
         # 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.
@@ -929,7 +982,7 @@ foreach my $file (@ARGV) {
 
     # 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
@@ -939,13 +992,12 @@ foreach my $file (@ARGV) {
         my ($abi, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch);
 
         # Disable unsupported hardening options.
-        if ($os !~ /^(?:linux|knetbsd|hurd)$/ or
-                $cpu =~ /^(?:hppa|mips|mipsel|avr32)$/) {
+        if ($os !~ /^(?:linux|knetbsd|hurd)$/ or $cpu =~ /^(?:hppa|avr32)$/) {
             $harden_pie = 0;
         }
-        if ($cpu =~ /^(?:ia64|alpha|mips|mipsel|hppa|arm64)$/
-                or $arch eq 'arm') {
+        if ($cpu =~ /^(?:ia64|alpha|hppa)$/ or $arch eq 'arm') {
             $harden_stack = 0;
+            $harden_stack_strong = 0;
         }
         if ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
             $harden_relro   = 0;
@@ -964,7 +1016,10 @@ foreach my $file (@ARGV) {
         @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);
     }
@@ -1150,6 +1205,14 @@ LINE:
             $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]);
+            remove_flags([\@cppflags], \%flag_renames, $def_cppflags_fortify[0]);
+        }
+
         # Check hardening flags.
         my @missing;
         if ($compile and not all_flags_used($line, \@missing, @cflags)
@@ -1509,7 +1572,7 @@ E<lt>jari.aalto@cante.netE<gt> for their valuable input and suggestions.
 
 =head1 LICENSE AND COPYRIGHT
 
-Copyright (C) 2012-2013 by Simon Ruderich
+Copyright (C) 2012-2015 by Simon Ruderich
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by