]> ruderich.org/simon Gitweb - blhc/blhc.git/blobdiff - bin/blhc
Strip (basic) environment variables before compiler detection
[blhc/blhc.git] / bin / blhc
index 2ea54d4e8f35bf2e0bd60dafd96ab078e44b2b93..0c9360b360788163294b89e94b9832091c05b98c 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-2020  Simon Ruderich
+# Copyright (C) 2012-2021  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
@@ -341,18 +341,24 @@ my $option_color;
 
 # FUNCTIONS
 
-# Only works for single-level arrays with no undef values. Thanks to perlfaq4.
-sub array_equal {
-    my ($first_ref, $second_ref) = @_;
+sub split_line {
+    my ($line) = @_;
 
-    return 0 if scalar @{$first_ref} != scalar @{$second_ref};
-
-    my $length = scalar @{$first_ref};
-    for (my $i = 0; $i < $length; $i++) {
-        return 0 if $first_ref->[$i] ne $second_ref->[$i];
+    my @work = ($line);
+    foreach my $delim (';', '&&', '||') {
+        my @x;
+        foreach (@work) {
+            push @x, Text::ParseWords::parse_line(qr/\Q$delim\E/, 1, $_);
+        }
+        @work = @x;
     }
 
-    return 1;
+    return map {
+        # Ensure newline at the line end - necessary for
+        # correct parsing later.
+        $_ =~ s/\s+$//;
+        $_ .= "\n";
+    } @work;
 }
 
 sub error_flags {
@@ -610,7 +616,7 @@ sub compile_flag_regexp {
     my @result = ();
     foreach my $flag (@flags) {
         # Compile flag regexp for faster execution.
-        my $regex = qr/\s$flag(?:\s|\\)/;
+        my $regex = qr/\s(['"]?)$flag\1(?:\s|\\)/;
 
         # Store flag name in replacement string for correct flags in messages
         # with qr//ed flag regexps.
@@ -684,7 +690,7 @@ if ($option_help) {
 }
 if ($option_version) {
     print <<"EOF";
-blhc $VERSION  Copyright (C) 2012-2020  Simon Ruderich
+blhc $VERSION  Copyright (C) 2012-2021  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
@@ -985,16 +991,12 @@ foreach my $file (@ARGV) {
         $non_verbose |= is_non_verbose_build($line, \$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.
-        my @line = (index($line, ';') == -1)
+        # Treat each command as a single line so we don't ignore valid
+        # commands when handling false positives. split_line() is slow, only
+        # use it when necessary.
+        my @line = ($line !~ /(?:;|&&|\|\|)/)
                  ? ($line)
-                 : map {
-                       # Ensure newline at the line end - necessary for
-                       # correct parsing later.
-                       $_ =~ s/\s+$//;
-                       $_ .= "\n";
-                   } Text::ParseWords::parse_line(';', 1, $line);
+                 : split_line($line);
         foreach my $line (@line) {
             if ($continuation) {
                 $continuation = 0;
@@ -1020,9 +1022,29 @@ foreach my $file (@ARGV) {
                 $complete_line = undef;
             }
 
+            my $noenv = $line;
+            # Strip (basic) environment variables for compiler detection. This
+            # prevents false positives when environment variables contain
+            # compiler binaries. Nested quotes, command substitution, etc. is
+            # not supported.
+            $noenv =~ s/^
+                \s*
+                (?:
+                    [a-zA-Z_]+          # environment variable name
+                    =
+                    (?:
+                        [^\s"'\$`\\]+   # non-quoted string
+                        |
+                        '[^"'\$`\\]*'   # single-quoted string
+                        |
+                        "[^"'\$`\\]*"   # double-quoted string
+                    )
+                    \s+
+                )*
+            //x;
             # Ignore lines with no compiler commands.
             next if not $non_verbose
-                    and not $line =~ /$cc_regex_normal/o;
+                    and not $noenv =~ /$cc_regex_normal/o;
             # Ignore lines with no filenames with extensions. May miss some
             # non-verbose builds (e.g. "gcc -o test" [sic!]), but shouldn't be
             # a problem as the log will most likely contain other non-verbose
@@ -1043,6 +1065,11 @@ foreach my $file (@ARGV) {
                                 # optional compiler options, don't allow
                                 # "everything" here to prevent false negatives
                                 \s*(?:\s-\S+)*\s*$}xo;
+            # `echo` is never a compiler command
+            next if $line =~ /^\s*echo\s/;
+            # Ignore calls to `make` because they can contain environment
+            # variables which look like compiler commands, e.g. CC=).
+            next if $line =~ /^\s*make\s/;
             # `moc-qt4`/`moc-qt5` contain '-I.../linux-g++' in their command
             # line (or similar for other architectures) which gets recognized
             # as a compiler line, but `moc-qt*` is only a preprocessor for Qt
@@ -1080,6 +1107,8 @@ foreach my $file (@ARGV) {
             next if $line =~ /^C\+\+ linker for the host machine: /;
             # Embedded `gcc -print-*` commands
             next if $line =~ /`$cc_regex_normal\s*[^`]*-print-\S+`/;
+            # cmake checking for compiler flags without setting CPPFLAGS
+            next if $line =~ m{^\s*/usr/(bin|lib)/(ccache/)?c\+\+ -dM -E -c /usr/share/cmake-\S+/Modules/CMakeCXXCompilerABI\.cpp};
 
             # Check if additional hardening options were used. Used to ensure
             # they are used for the complete build.
@@ -1543,6 +1572,9 @@ you find false positives which affect more packages please report a bug.
 
 To generate this string simply use echo in C<debian/rules>; make sure to use @
 to suppress the echo command itself as it could also trigger a false positive.
+If the build process takes a long time edit the C<.build> file in place and
+tweak the ignore string until B<blhc --all --debian package.build> no longer
+reports any false positives.
 
 =head1 OPTIONS