X-Git-Url: https://ruderich.org/simon/gitweb/?p=blhc%2Fblhc.git;a=blobdiff_plain;f=bin%2Fblhc;h=e918ee85c3e713a96145b5cf439dc4be47f11e21;hp=9869d2cec50889261b83822dead8fb1db28a2b9c;hb=7f5037cc186261f1ecd243b6b432f01b3689c2a0;hpb=c1d4e05b30639164e1bbcaa8fcdd96abe67683db diff --git a/bin/blhc b/bin/blhc index 9869d2c..e918ee8 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-2014 Simon Ruderich +# Copyright (C) 2012-2016 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.06'; # CONSTANTS/VARIABLES @@ -38,8 +38,11 @@ my $cc_regex = qr/ /x; # Full regex which matches the complete compiler name. Used in a few places to # prevent false negatives. +my $cc_regex_full_prefix = qr/ + [a-z0-9_]+-(?:linux-|kfreebsd-)?gnu(?:eabi|eabihf)? + /x; my $cc_regex_full = qr/ - (?:[a-z0-9_]+-(?:linux-|kfreebsd-)?gnu(?:eabi|eabihf)?-)? + (?:$cc_regex_full_prefix-)? $cc_regex /x; # Regex to check if a line contains a compiler command. @@ -51,6 +54,7 @@ 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=/; +my $libtool_link_regex = qr/\blibtool: link: /; # List of source file extensions which require preprocessing. my @source_preprocess_compile_cpp = ( @@ -441,7 +445,7 @@ sub pic_pie_conflict { } sub is_non_verbose_build { - my ($line, $next_line, $skip_ref) = @_; + my ($line, $skip_ref, $input_ref, $line_offset, $line_count) = @_; if ($line =~ /$libtool_regex/o) { # libtool's --silent hides the real compiler flags. @@ -481,25 +485,30 @@ sub is_non_verbose_build { my $file = $1; # On the first pass we only check if this line is verbose or not. - return 1 if not defined $next_line; + return 1 if not defined $input_ref; - # Second pass, we have access to the next line. + # Second pass, we have access to the next lines. ${$skip_ref} = 0; # CMake and other build systems print the non-verbose messages also when # building verbose. If a compiler and the file name occurs in the next - # line, treat it as verbose build. + # lines, treat it as verbose build. if (defined $file) { # Get filename, we can't use the complete path as only parts of it are # used in the real compiler command. $file =~ m{/([^/\s]+)$}; $file = $1; - if (index($next_line, $file) != -1 and $next_line =~ /$cc_regex/o) { - # Not a non-verbose line, but we still have to skip the current line - # as it doesn't contain any compiler commands. - ${$skip_ref} = 1; - return 0; + for (my $i = 1; $i <= $line_count; $i++) { + my $next_line = $input_ref->[$line_offset + $i]; + last unless defined $next_line; + + if (index($next_line, $file) != -1 and $next_line =~ /$cc_regex/o) { + # Not a non-verbose line, but we still have to skip the + # current line as it doesn't contain any compiler commands. + ${$skip_ref} = 1; + return 0; + } } } @@ -601,7 +610,7 @@ if ($option_help) { } if ($option_version) { print <<"EOF"; -blhc $VERSION Copyright (C) 2012-2014 Simon Ruderich +blhc $VERSION Copyright (C) 2012-2016 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 @@ -710,10 +719,15 @@ foreach my $file (@ARGV) { my $harden_pie = $option_pie; # defaults to 0 # Does this build log use ada? Ada also uses gcc as compiler but uses - # different CFLAGS. But only perform ada checks if an ada compiler used + # different CFLAGS. But only perform ada checks if an ada compiler is used # for performance reasons. my $ada = 0; + # Number of parallel jobs to prevent false positives when detecting + # non-verbose builds. As not all jobs declare the number of parallel jobs + # use a large enough default. + my $parallel = 10; + while (my $line = <$fh>) { # Detect architecture automatically unless overridden. For buildd logs # only, doesn't use the dpkg-buildpackage header. Necessary to ignore @@ -807,6 +821,11 @@ foreach my $file (@ARGV) { } } + # This flags is not always available, but if it is use it. + if ($line =~ /^DEB_BUILD_OPTIONS=.*\bparallel=(\d+)/) { + $parallel = $1; + } + # We skip over unimportant lines at the beginning of the log to # prevent false positives. last if index($line, 'dpkg-buildpackage: ') == 0; @@ -846,6 +865,7 @@ foreach my $file (@ARGV) { } } + next if $line =~ /^\s*#/; # Ignore compiler warnings for now. next if $line =~ /$warning_regex/o; @@ -863,7 +883,7 @@ foreach my $file (@ARGV) { # Check if this line indicates a non verbose build. my $skip = 0; - $non_verbose |= is_non_verbose_build($line, undef, \$skip); + $non_verbose |= is_non_verbose_build($line, \$skip); next if $skip; # One line may contain multiple commands (";"). Treat each one as @@ -942,6 +962,16 @@ foreach my $file (@ARGV) { next if not $before =~ /$cc_regex_normal/o and not $after =~ /$cc_regex_normal/o; } + # Ignore false positives caused by gcc -v. It outputs a line + # looking like a normal compiler line but which is sometimes + # missing hardening flags, although the normal compiler line + # contains them. + next if $line =~ m{^\s+/usr/lib/gcc/$cc_regex_full_prefix/ + [0-9.]+/cc1(?:plus)?}xo; + # Ignore false positive with `rm` which may remove files which + # look like a compiler executable thus causing the line to be + # treated as a normal compiler line. + next if $line =~ m{^\s*rm\s+}; # Check if additional hardening options were used. Used to ensure # they are used for the complete build. @@ -982,7 +1012,7 @@ foreach my $file (@ARGV) { # Option or auto detected. if ($arch) { - # The following was partially copied from dpkg-dev 1.17.11 + # The following was partially copied from dpkg-dev 1.18.7 # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, add_hardening_flags()), # copyright Raphaël Hertzog , Kees Cook # , Canonical, Ltd. licensed under GPL version 2 or @@ -992,11 +1022,10 @@ 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)$/ or $arch eq 'arm') { + if ($cpu =~ /^(?:ia64|alpha|hppa|nios2)$/ or $arch eq 'arm') { $harden_stack = 0; $harden_stack_strong = 0; } @@ -1089,7 +1118,8 @@ LINE: my $skip = 0; if ($input_nonverbose[$i] - and is_non_verbose_build($line, $input[$i + 1], \$skip)) { + and is_non_verbose_build($line, \$skip, + \@input, $i, $parallel)) { if (not $option_buildd) { error_non_verbose_build($line); $exit |= $exit_code{non_verbose_build}; @@ -1102,6 +1132,8 @@ LINE: # is_non_verbose_build()). next if $skip; + my $orig_line = $line; + # Remove everything until and including the compiler command. Makes # checks easier and faster. $line =~ s/^.*?$cc_regex//o; @@ -1152,7 +1184,11 @@ LINE: } # These file types require preprocessing. if (extension_found(\%extensions_preprocess, @extensions)) { - $preprocess = 1; + # Prevent false positives with "libtool: link: g++ -include test.h + # .." compiler lines. + if ($orig_line !~ /$libtool_link_regex/o) { + $preprocess = 1; + } } if (not $flag_preprocess) { @@ -1573,7 +1609,7 @@ Ejari.aalto@cante.netE for their valuable input and suggestions. =head1 LICENSE AND COPYRIGHT -Copyright (C) 2012-2014 by Simon Ruderich +Copyright (C) 2012-2016 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