X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=bin%2Fblhc;h=465ce0fc04d4708fd5fbdab00f5aed68206a6d3c;hb=46cd48288d8300d525282dc31926108f264ee985;hp=2c439bb0f33003817eb0fd8e1c7be09a33def19a;hpb=cdecc177aceddaba33f6414081f9f9137933a846;p=blhc%2Fblhc.git diff --git a/bin/blhc b/bin/blhc index 2c439bb..465ce0f 100755 --- a/bin/blhc +++ b/bin/blhc @@ -23,6 +23,7 @@ use warnings; use Getopt::Long (); use Term::ANSIColor (); +use Text::ParseWords (); our $VERSION = '0.01'; @@ -30,12 +31,87 @@ our $VERSION = '0.01'; # CONSTANTS/VARIABLES # Regex to catch compiler commands. -my $cc_regex = qr/(?:[a-z0-9_]+-(?:linux|kfreebsd)-gnu(?:eabi|eabihf)?-)? - (?:(?) { } } - # We skip over unimportant lines at the beginning to prevent false - # positives. + # If hardening wrapper is used (wraps calls to gcc and adds hardening + # flags automatically) we can't perform any checks, abort. + if (not $start and $line =~ /^Build-Depends: .*\bhardening-wrapper\b/) { + error_hardening_wrapper(); + $exit |= 1 << 4; + exit $exit; + } + + # We skip over unimportant lines at the beginning of the log to prevent + # false positives. $start = 1 if $line =~ /^dpkg-buildpackage:/; next if not $start; + # 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 + # positives. + last if $line =~ /^Build finished at \d{8}-\d{4}$/; # Detect architecture automatically unless overridden. if (not $option_arch @@ -309,7 +401,7 @@ while (my $line = <>) { # Remove all ANSI color sequences which are sometimes used in non-verbose # builds. $line = Term::ANSIColor::colorstrip($line); - # Also strip '\0xf' (delete previous character), used by Elink's build + # Also strip '\0xf' (delete previous character), used by Elinks' build # system. $line =~ s/\x0f//g; # And "ESC(B" which seems to be used on armhf and hurd (not sure what it @@ -320,8 +412,10 @@ while (my $line = <>) { my $non_verbose = is_non_verbose_build($line); # One line may contain multiple commands (";"). Treat each one as single - # line. - my @line = split /(?) { # Ignore false positives. # # `./configure` output. - next if not $non_verbose and $line =~ /^checking /; - next if $line =~ /^\s*(?:C\s+)? - (?:C|c)ompiler[\s.]*:\s+ + next if not $non_verbose + and $line =~ /^(?:checking|(?:C|c)onfigure:) /; + next if $line =~ /^\s*(?:Host\s+)?(?:C\s+)? + (?:C|c)ompiler[\s.]*:?\s+ $cc_regex (?:\s-std=[a-z0-9:+]+)?\s*$ /x - or $line =~ /^\s*(?:- )?(?:CC|CXX)\s*=\s*$cc_regex\s*$/ + or $line =~ /^\s*(?:- )?(?:HOST_)?(?:CC|CXX)\s*=\s*$cc_regex\s*$/ or $line =~ /^\s*-- Check for working (?:C|CXX) compiler: / or $line =~ /^\s*(?:echo )?Using [A-Z_]+\s*=\s*/; - # Debian buildd output. - next if $line =~ /^\s*Depends: .*?$cc_regex.*?$/ - and $line !~ /\s-./; # option, prevent false negatives + # Check if additional hardening options were used. Used to ensure + # they are used for the complete build. + $harden_pie = 1 if any_flags_used($line, @cflags_pie, @ldflags_pie); + $harden_bindnow = 1 if any_flags_used($line, @ldflags_bindnow); push @input, $line; } @@ -404,13 +500,6 @@ if ($option_arch) { } } -# Check if additional hardening options were used. Used to ensure they are -# used for the complete build. -foreach my $line (@input) { - $harden_pie = 1 if any_flags_used($line, @cflags_pie, @ldflags_pie); - $harden_bindnow = 1 if any_flags_used($line, @ldflags_bindnow); -} - # Check the specified hardening options, same order as dpkg-buildflags. if ($harden_pie) { @cflags = (@cflags, @cflags_pie); @@ -445,49 +534,68 @@ for (my $i = 0; $i < scalar @input; $i++) { # Even if it's a verbose build, we might have to skip this line. next if $skip; + # Skip unnecessary tests when only preprocessing. + my $flag_preprocess = 0; - # Is this a compiler or linker command? - my $compiler = 1; - my $linker = 0; - - # Linker commands. - if ($line =~ m{\s-o # -o - [\s\\]*\s+ # possible line continuation - (?:[/.A-Za-z0-9~_-]+/)? # path to file - [A-Za-z0-9~_-]+ # binary name (no dots!) - (?:[0-9.]*\.so[0-9.]*[a-z]? # library (including version) - |\.la - |\.cgi)? # CGI binary - (?:\s|\\|$) # end of file name - }x - or $line =~ /^libtool: link: / - or $line =~ m{\s*/bin/bash .+?libtool\s+(.+?\s+)?--mode=(re)?link}) { - $compiler = 0; - $linker = 1; + my $preprocess = 0; + my $compile = 0; + my $link = 0; + + # Preprocess, compile, assemble. + if ($line =~ /$cc_regex.*?\s(-E|-S|-c)\b/) { + $preprocess = 1; + $flag_preprocess = 1 if $1 eq '-E'; + $compile = 1 if $1 eq '-S' or $1 eq '-c'; + # Otherwise assume we are linking. + } else { + $link = 1; + } + + # These file types don't require preprocessing. + if ($line =~ /$file_no_preprocess_regex/) { + $preprocess = 0; + } + # These file types require preprocessing. + if ($line =~ /$file_preprocess_regex/) { + $preprocess = 1; } # If there are source files then it's compiling/linking in one step and we - # must check both. - if ($line =~ /\.(?:c|cc|cpp)\b/) { - $compiler = 1; + # must check both. We only check for source files here, because header + # files cause too many false positives. + if (not $flag_preprocess and $line =~ /$file_compile_link_regex/) { + # Assembly files don't need CFLAGS. + if (not $line =~ /$file_compile_regex/ + and $line =~ /$file_no_compile_regex/) { + $compile = 0; + # But the rest does. + } else { + $compile = 1; + } } # Check hardening flags. my @missing; - if ($compiler and not all_flags_used($line, \@missing, @cflags) + if ($compile and not all_flags_used($line, \@missing, @cflags) # Libraries linked with -fPIC don't have to (and can't) be linked # with -fPIE as well. It's no error if only PIE flags are missing. - and not pic_pie_conflict($line, $harden_pie, \@missing, @cflags_pie)) { + and not pic_pie_conflict($line, $harden_pie, \@missing, @cflags_pie) + # Assume dpkg-buildflags returns the correct flags. + and not $line =~ /`dpkg-buildflags --get (?:CFLAGS|CXXFLAGS)`/) { error_flags('CFLAGS missing', \@missing, \%flag_renames, $line); $exit |= 1 << 3; } - if ($compiler and not all_flags_used($line, \@missing, @cppflags)) { + if ($preprocess and not all_flags_used($line, \@missing, @cppflags) + # Assume dpkg-buildflags returns the correct flags. + and not $line =~ /`dpkg-buildflags --get CPPFLAGS`/) { error_flags('CPPFLAGS missing', \@missing, \%flag_renames, $line); $exit |= 1 << 3; } - if ($linker and not all_flags_used($line, \@missing, @ldflags) + if ($link and not all_flags_used($line, \@missing, @ldflags) # Same here, -fPIC conflicts with -fPIE. - and not pic_pie_conflict($line, $harden_pie, \@missing, @ldflags_pie)) { + and not pic_pie_conflict($line, $harden_pie, \@missing, @ldflags_pie) + # Assume dpkg-buildflags returns the correct flags. + and not $line =~ /`dpkg-buildflags --get LDFLAGS`/) { error_flags('LDFLAGS missing', \@missing, \%flag_renames, $line); $exit |= 1 << 3; } @@ -599,6 +707,10 @@ Non verbose build. Missing hardening flags. +=item B<16> + +Hardening wrapper detected, no tests performed. + =back =head1 AUTHOR