X-Git-Url: https://ruderich.org/simon/gitweb/?p=blhc%2Fblhc.git;a=blobdiff_plain;f=bin%2Fblhc;h=cfdcbba0fd103770b0b0a068d56fe0c1223083c5;hp=73b770df9e210c0f2139a62bc7622570a5890e71;hb=290a8e3484c700ebb91c3460820310e03ca38cb2;hpb=a00399d7dce651ac647634b0ac6b4a6056a92f97 diff --git a/bin/blhc b/bin/blhc index 73b770d..cfdcbba 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-2015 Simon Ruderich +# Copyright (C) 2012-2017 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.05'; +our $VERSION = '0.07'; # CONSTANTS/VARIABLES @@ -33,7 +33,7 @@ our $VERSION = '0.05'; my $cc_regex = qr/ (? 1 } ( @source_preprocess_compile_cpp, @source_no_preprocess_compile_cpp, ); +my %extensions_ada = map { $_ => 1 } ( + @source_no_preprocess_compile_ada, + @source_no_preprocess_no_compile_ada, +); +my %extensions_fortran = map { $_ => 1 } ( + @source_no_preprocess_compile_fortran, + @source_preprocess_compile_fortran, +); my %extensions_object = map { $_ => 1 } ( @object, ); @@ -445,7 +465,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. @@ -485,25 +505,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; + } } } @@ -605,7 +630,7 @@ if ($option_help) { } if ($option_version) { print <<"EOF"; -blhc $VERSION Copyright (C) 2012-2015 Simon Ruderich +blhc $VERSION Copyright (C) 2012-2017 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 @@ -717,6 +742,17 @@ foreach my $file (@ARGV) { # different CFLAGS. But only perform ada checks if an ada compiler is used # for performance reasons. my $ada = 0; + # Fortran also requires different CFLAGS. + my $fortran = 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; + + # Don't check for PIE flags if automatically applied by the compiler. Only + # used in buildd mode. + my $disable_harden_pie = 0; while (my $line = <$fh>) { # Detect architecture automatically unless overridden. For buildd logs @@ -740,6 +776,10 @@ foreach my $file (@ARGV) { # flags are not checked. # # Strong stack protector is used since dpkg 1.17.11. + # + # Recent GCC versions automatically use PIE (only on supported + # architectures) and dpkg respects this properly since 1.18.15 and + # doesn't pass PIE flags manually. if ($option_buildd and index($line, 'Toolchain package versions: ') == 0) { require Dpkg::Version; @@ -754,6 +794,9 @@ foreach my $file (@ARGV) { if (Dpkg::Version::version_compare($1, '1.17.11') >= 0) { $disable_strong = 0; } + if (Dpkg::Version::version_compare($1, '1.18.15') >= 0) { + $disable_harden_pie = 1; + } } if ($disable) { @@ -809,6 +852,15 @@ foreach my $file (@ARGV) { if ($line =~ /\bgnat\b/) { $ada = 1; } + # Fortran compiler. + if ($line =~ /\bgfortran\b/) { + $fortran = 1; + } + } + + # 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 @@ -850,6 +902,7 @@ foreach my $file (@ARGV) { } } + next if $line =~ /^\s*#/; # Ignore compiler warnings for now. next if $line =~ /$warning_regex/o; @@ -867,7 +920,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 @@ -956,6 +1009,8 @@ foreach my $file (@ARGV) { # look like a compiler executable thus causing the line to be # treated as a normal compiler line. next if $line =~ m{^\s*rm\s+}; + # Some build systems emit "gcc > file". + next if $line =~ m{$cc_regex_normal\s*>\s*\S+}; # Check if additional hardening options were used. Used to ensure # they are used for the complete build. @@ -996,17 +1051,30 @@ foreach my $file (@ARGV) { # Option or auto detected. if ($arch) { - # The following was partially copied from dpkg-dev 1.18.7 - # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, add_hardening_flags()), + # The following was partially copied from dpkg-dev 1.18.24 + # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, _add_hardening_flags()), # copyright Raphaël Hertzog , Kees Cook # , Canonical, Ltd. licensed under GPL version 2 or # later. Keep it in sync. require Dpkg::Arch; - my ($abi, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch); + my ($os, $cpu); + # Recent dpkg versions use a quadruplet for arch. Support both. + eval { + (undef, undef, $os, $cpu) = Dpkg::Arch::debarch_to_debtuple($arch); + }; + if ($@) { + (undef, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch); + } + + my %builtin_pie_arch = map { $_ => 1 } qw( + amd64 arm64 armel armhf i386 kfreebsd-amd64 kfreebsd-i386 + mips mipsel mips64el ppc64el s390x sparc sparc64 + ); # Disable unsupported hardening options. - if ($os !~ /^(?:linux|knetbsd|hurd)$/ or $cpu =~ /^(?:hppa|avr32)$/) { + if ($os !~ /^(?:linux|kfreebsd|knetbsd|hurd)$/ + or $cpu =~ /^(?:hppa|avr32)$/) { $harden_pie = 0; } if ($cpu =~ /^(?:ia64|alpha|hppa|nios2)$/ or $arch eq 'arm') { @@ -1017,6 +1085,10 @@ foreach my $file (@ARGV) { $harden_relro = 0; $harden_bindnow = 0; } + + if ($disable_harden_pie and exists $builtin_pie_arch{$arch}) { + $harden_pie = 0; + } } # Default values. @@ -1053,14 +1125,13 @@ foreach my $file (@ARGV) { @ldflags = (@ldflags, @def_ldflags_bindnow); } - # Stores normal CFLAGS when @cflags_ada are temporarily used. - my @cflags_backup; - # Ada CFLAGS, only set if ada is used. - my @cflags_ada; # Ada doesn't support format hardening flags, see #680117 for more - # information. Filter them out if ada is used. - if ($ada and $harden_format) { - @cflags_ada = grep { + # information. Same for fortran. Filter them out if either language is + # used. + my @cflags_backup; + my @cflags_noformat; + if (($ada or $fortran) and $harden_format) { + @cflags_noformat = grep { my $ok = 1; foreach my $flag (@def_cflags_format) { $ok = 0 if $_ eq $flag; @@ -1102,7 +1173,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}; @@ -1201,7 +1273,7 @@ LINE: } my $compile_cpp = 0; - my $compile_ada = 0; + my $restore_cflags = 0; # Assume CXXFLAGS are required when a C++ file is specified in the # compiler line. if ($compile @@ -1210,12 +1282,17 @@ LINE: $compile_cpp = 1; # Ada needs special CFLAGS, use them if only ada files are compiled. } elsif ($ada - and $compile - and array_equal(\@extensions, - \@source_no_preprocess_compile_ada)) { - $compile_ada = 1; + and extension_found(\%extensions_ada, @extensions)) { + $restore_cflags = 1; + $preprocess = 0; # Ada uses no CPPFLAGS + @cflags_backup = @cflags; + @cflags = @cflags_noformat; + # Same for fortran. + } elsif ($fortran + and extension_found(\%extensions_fortran, @extensions)) { + $restore_cflags = 1; @cflags_backup = @cflags; - @cflags = @cflags_ada; + @cflags = @cflags_noformat; } if ($option_buildd) { @@ -1290,7 +1367,7 @@ LINE: } # Restore normal CFLAGS. - if ($compile_ada) { + if ($restore_cflags) { @cflags = @cflags_backup; } } @@ -1592,7 +1669,7 @@ Ejari.aalto@cante.netE for their valuable input and suggestions. =head1 LICENSE AND COPYRIGHT -Copyright (C) 2012-2015 by Simon Ruderich +Copyright (C) 2012-2017 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