]> ruderich.org/simon Gitweb - blhc/blhc.git/blobdiff - bin/blhc
Properly ignore cargo/rust output
[blhc/blhc.git] / bin / blhc
index 3611c6de4c3980fa743b201be179484705559590..2e48bbc063c6bc466ee6c6ceaae4eba42929e9b4 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-2024  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.12';
+our $VERSION = '0.13';
 
 
 # CONSTANTS/VARIABLES
@@ -49,6 +49,9 @@ my $cc_regex_full = qr/
 my $cc_regex_normal = qr/
     \b$cc_regex(?:\s|\\)
     /x;
+my $rustc_regex = qr/
+    \brustc\b
+    /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
@@ -206,7 +209,7 @@ my $file_extension_regex = qr/
 # Expected (hardening) flags. All flags are used as regexps (and compiled to
 # real regexps below for better execution speed).
 my @def_cflags = (
-    '-g',
+    '-g3?',
     '-O(?:2|3)', # keep at index 1, search for @def_cflags_debug to change it
 );
 my @def_cflags_debug = (
@@ -243,7 +246,7 @@ my @def_cxxflags = (
 # @def_cxxflags_* is the same as @def_cflags_*.
 my @def_cppflags = ();
 my @def_cppflags_fortify = (
-    '-D_FORTIFY_SOURCE=2', # must be first, see cppflags_fortify_broken()
+    '-D_FORTIFY_SOURCE=[23]', # must be first, see cppflags_fortify_broken()
     # If you add another flag fix hack below (search for "Hack to fix") and
     # $def_cppflags_fortify[0].
 );
@@ -296,9 +299,11 @@ my @flag_refs_all = (
 # Renaming rules for the output so the regex parts are not visible. Also
 # stores string values of flag regexps above, see compile_flag_regexp().
 my %flag_renames = (
+    '-g3?'                         => '-g',
     '-O(?:2|3)'                    => '-O2',
     '-Wformat(?:=2)?'              => '-Wformat',
     '--param[= ]ssp-buffer-size=4' => '--param=ssp-buffer-size=4',
+    '-D_FORTIFY_SOURCE=[23]'       => '-D_FORTIFY_SOURCE=2',
     '-Wl,(?:-z,)?relro'            => '-Wl,-z,relro',
     '-Wl,(?:-z,)?now'              => '-Wl,-z,now',
 );
@@ -520,7 +525,7 @@ sub pic_pie_conflict {
 }
 
 sub is_non_verbose_build {
-    my ($line, $skip_ref, $input_ref, $line_offset, $line_count) = @_;
+    my ($line, $cargo, $skip_ref, $input_ref, $line_offset, $line_count) = @_;
 
     if ($line =~ /$libtool_regex/o) {
         # libtool's --silent hides the real compiler flags.
@@ -552,6 +557,8 @@ sub is_non_verbose_build {
     # "Compiling" non binary files.
     return 0 if $line =~ /^\s*Compiling \S+\.(?:py|pyx|el)['"]?\s*(?:\.\.\.|because it changed\.)?$/;
     return 0 if $line =~ /^\s*[Cc]ompiling catalog \S+\.po\b/;
+    # cargo build
+    return 0 if $cargo and $line =~ m{^\s*Compiling\s+\S+\s+v\S+(?:\s+\(/<<PKGBUILDDIR>>\))?$};
     # "Compiling" with no file name.
     if ($line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/) {
         # $file_extension_regex may need spaces around the filename.
@@ -616,7 +623,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.
@@ -690,7 +697,7 @@ if ($option_help) {
 }
 if ($option_version) {
     print <<"EOF";
-blhc $VERSION  Copyright (C) 2012-2020  Simon Ruderich
+blhc $VERSION  Copyright (C) 2012-2024  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
@@ -811,6 +818,9 @@ foreach my $file (@ARGV) {
         $disable_harden_pie = 1;
     }
 
+    # Ignore additional false positives if cargo/rust is used
+    my $cargo = 0;
+
     my $number = 0;
     while (my $line = <$fh>) {
         $number++;
@@ -907,6 +917,10 @@ foreach my $file (@ARGV) {
                 }
                 next FILE;
             }
+
+            if ($line =~ /\bcargo\b/) {
+                $cargo = 1;
+            }
         }
 
         # This flags is not always available, but if it is use it.
@@ -988,7 +1002,7 @@ foreach my $file (@ARGV) {
 
         # Check if this line indicates a non verbose build.
         my $skip = 0;
-        $non_verbose |= is_non_verbose_build($line, \$skip);
+        $non_verbose |= is_non_verbose_build($line, $cargo, \$skip);
         next if $skip;
 
         # Treat each command as a single line so we don't ignore valid
@@ -1022,9 +1036,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
@@ -1056,10 +1090,12 @@ foreach my $file (@ARGV) {
             # C++ files. No hardening flags are relevant during this step,
             # thus ignore `moc-qt*` lines. The resulting files will be
             # compiled in a separate step (and therefore checked).
-            next if $line =~ m{^\S+/bin/moc(?:-qt[45])?
+            next if $line =~ m{^\S+(?:/bin/moc(?:-qt[45])?|/lib/qt6/libexec/moc)
                                \s.+\s
                                -I\S+/mkspecs/[a-z]+-g\++(?:-64)?
                                \s}x;
+            # nvcc is not a regular C compiler
+            next if $line =~ m{^\S+/bin/nvcc\s};
             # Ignore false positives when the line contains only CC=gcc but no
             # other gcc command.
             if ($line =~ /(.*)CC=$cc_regex_full(.*)/o) {
@@ -1087,6 +1123,10 @@ 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};
+            # Some rustc lines look like linker commands
+            next if $cargo and $line =~ /$rustc_regex/o;
 
             # Check if additional hardening options were used. Used to ensure
             # they are used for the complete build.
@@ -1128,11 +1168,12 @@ foreach my $file (@ARGV) {
 
     # Option or auto detected.
     if ($arch) {
-        # The following was partially copied from dpkg-dev 1.20.5
-        # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, _add_build_flags()),
-        # copyright Raphaël Hertzog <hertzog@debian.org>, Guillem Jover
-        # <guillem@debian.org>, Kees Cook <kees@debian.org>, Canonical, Ltd.
-        # licensed under GPL version 2 or later. Keep it in sync.
+        # The following was partially copied from dpkg-dev 1.22.0
+        # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, set_build_features and
+        # _add_build_flags()), copyright Raphaël Hertzog <hertzog@debian.org>,
+        # Guillem Jover <guillem@debian.org>, Kees Cook <kees@debian.org>,
+        # Canonical, Ltd. licensed under GPL version 2 or later. Keep it in
+        # sync.
 
         require Dpkg::Arch;
         my ($os, $cpu);
@@ -1149,13 +1190,23 @@ foreach my $file (@ARGV) {
             arm64
             armel
             armhf
+            hurd-amd64
             hurd-i386
             i386
             kfreebsd-amd64
             kfreebsd-i386
             mips
-            mipsel
+            mips64
             mips64el
+            mips64r6
+            mips64r6el
+            mipsel
+            mipsn32
+            mipsn32el
+            mipsn32r6
+            mipsn32r6el
+            mipsr6
+            mipsr6el
             powerpc
             ppc64
             ppc64el
@@ -1166,15 +1217,14 @@ foreach my $file (@ARGV) {
         );
 
         # Disable unsupported hardening options.
-        if ($os !~ /^(?:linux|kfreebsd|knetbsd|hurd)$/
-                or $cpu =~ /^(?:hppa|avr32)$/) {
+        if ($os !~ /^(?:linux|kfreebsd|knetbsd|hurd)$/ or $cpu eq 'hppa') {
             $harden_pie = 0;
         }
         if ($cpu =~ /^(?:ia64|alpha|hppa|nios2)$/ or $arch eq 'arm') {
             $harden_stack = 0;
             $harden_stack_strong = 0;
         }
-        if ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
+        if ($cpu =~ /^(?:ia64|hppa)$/) {
             $harden_relro   = 0;
             $harden_bindnow = 0;
         }
@@ -1262,7 +1312,7 @@ LINE:
 
         my $skip = 0;
         if ($input_nonverbose[$i]
-                and is_non_verbose_build($line, \$skip,
+                and is_non_verbose_build($line, $cargo, \$skip,
                                          \@input, $i, $parallel)) {
             if (not $option_buildd) {
                 error_non_verbose_build($line, $input_number[$i]);
@@ -1543,13 +1593,17 @@ log:
     blhc: ignore-line-regexp: REGEXP
 
 All lines fully matching REGEXP (see B<--ignore-line> for details) will be
-ignored.
+ignored. The string can be embedded multiple times to ignore different
+regexps.
 
 Please use this feature sparingly so that missing flags are not overlooked. If
 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
 
@@ -1796,7 +1850,7 @@ E<lt>jari.aalto@cante.netE<gt> for their valuable input and suggestions.
 
 =head1 LICENSE AND COPYRIGHT
 
-Copyright (C) 2012-2020 by Simon Ruderich
+Copyright (C) 2012-2024 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