- # Ignore lines with no compiler commands.
- next if not $non_verbose
- and not $line =~ /\b$cc_regex(?:\s|\\)/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 commands which are detected.
- next if not $non_verbose
- and not $line =~ /$file_extension_regex/o;
-
- # Ignore false positives.
- #
- # `./configure` output.
- next if not $non_verbose
- and $line =~ /^(?:checking|(?:C|c)onfigure:) /;
- next if $line =~ /^\s*(?:Host\s+)?(?:C(?:\+\+)?\s+)?
- (?:C|c)ompiler[\s.]*:?\s+
- /xo;
- next if $line =~ /^\s*(?:- )?(?:HOST_)?(?:CC|CXX)\s*=\s*$cc_regex_full\s*$/o;
-
- # 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, @def_cflags_pie, @def_ldflags_pie);
- $harden_bindnow = 1 if any_flags_used($line, @def_ldflags_bindnow);
-
- push @input, $line;
+ 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 $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
+ # commands which are detected.
+ next if not $non_verbose
+ and not $line =~ /$file_extension_regex/o;
+
+ # Ignore false positives.
+ #
+ # `./configure` output.
+ next if not $non_verbose
+ and $line =~ /^(?:checking|[Cc]onfigure:) /;
+ next if $line =~ /^\s*(?:Host\s+)?(?:C(?:\+\+)?\s+)?
+ [Cc]ompiler[\s.]*:?\s+
+ /x;
+ next if $line =~ m{^\s*(?:-\s)?(?:HOST_)?(?:CC|CXX)
+ \s*=\s*$cc_regex_full
+ # 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
+ # 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])?|/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) {
+ my $before = $1;
+ my $after = $2;
+ 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+};
+ next if $line =~ m{^\s*dwz\s+};
+ # Some build systems emit "gcc > file".
+ next if $line =~ m{$cc_regex_normal\s*>\s*\S+}o;
+ # Hex output may contain "cc".
+ next if $line =~ m#(?:\b[0-9a-fA-F]{2,}\b\s*){5}#;
+ # Meson build output
+ 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\+\+ (?:-std=\S+ )?-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.
+ $harden_pie = 1 if any_flags_used($line, @def_cflags_pie,
+ @def_ldflags_pie);
+ $harden_bindnow = 1 if any_flags_used($line, @def_ldflags_bindnow);
+
+ push @input, $line;
+ push @input_nonverbose, $non_verbose;
+ push @input_number, $number if $option_line_numbers;