X-Git-Url: https://ruderich.org/simon/gitweb/?p=blhc%2Fblhc.git;a=blobdiff_plain;f=bin%2Fblhc;h=d2a8771515e5e01d8a3890db542cb7cf08244f21;hp=b45439ca9e533a7e01fbb2bc8759d5be8571d29e;hb=7d9ae6314e499d9e76bc32a0e1c4a5686bb59158;hpb=b12aec7229ebf8aa903aac01f66cab2d061b0a01 diff --git a/bin/blhc b/bin/blhc index b45439c..d2a8771 100755 --- 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 @@ -816,7 +862,9 @@ foreach my $file (@ARGV) { } # Check if this line indicates a non verbose build. - $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. @@ -934,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 , Kees Cook # , Canonical, Ltd. licensed under GPL version 2 or @@ -944,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; @@ -969,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); } @@ -1155,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) @@ -1514,7 +1572,7 @@ Ejari.aalto@cante.netE 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