3 # Build log hardening check, checks build logs for missing hardening flags.
5 # Copyright (C) 2012-2024 Simon Ruderich
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 use Text::ParseWords ();
27 our $VERSION = '0.14';
32 # Regex to catch compiler commands.
34 (?<!\s-) # ignore options, e.g. "-c++" [sic!] (used by swig)
35 (?<!\.) # ignore file names, e.g. "test.gcc"
36 (?:cc|gcc|g\+\+|c\+\+|gfortran|mpicc|mpicxx|mpifort)
37 (?:-[\d.]+)? # version suffix, e.g. "gcc-4.6"
39 # Full regex which matches the complete compiler name. Used in a few places to
40 # prevent false negatives.
41 my $cc_regex_full_prefix = qr/
42 [a-z0-9_]+-(?:linux-|kfreebsd-)?gnu(?:eabi|eabihf)?
44 my $cc_regex_full = qr/
45 (?:$cc_regex_full_prefix-)?
48 # Regex to check if a line contains a compiler command.
49 my $cc_regex_normal = qr/
55 # Regex to catch (GCC) compiler warnings.
56 my $warning_regex = qr/^(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]$/;
57 # Regex to catch libtool commands and not lines which show commands executed
58 # by libtool (e.g. libtool: link: ...).
59 my $libtool_regex = qr/\blibtool["']?\s.*--mode=/;
60 my $libtool_link_regex = qr/\blibtool: link: /;
62 # List of source file extensions which require preprocessing.
63 my @source_preprocess_compile_cpp = (
65 qw( cc cp cxx cpp CPP c++ C ),
69 my @source_preprocess_compile_fortran = (
71 qw( F FOR fpp FPP FTN F90 F95 F03 F08 ),
73 my @source_preprocess_compile = (
79 @source_preprocess_compile_cpp,
81 @source_preprocess_compile_fortran,
83 my @source_preprocess_no_compile = (
87 my @source_preprocess = (
88 @source_preprocess_compile,
89 @source_preprocess_no_compile,
91 # List of source file extensions which don't require preprocessing.
92 my @source_no_preprocess_compile_cpp = (
98 my @source_no_preprocess_compile_ada = (
104 my @source_no_preprocess_compile_fortran = (
106 qw( f for ftn f90 f95 f03 f08 ),
108 my @source_no_preprocess_compile = (
112 @source_no_preprocess_compile_cpp,
116 @source_no_preprocess_compile_fortran,
118 @source_no_preprocess_compile_ada,
120 my @source_no_preprocess_no_compile_ada = (
124 my @source_no_preprocess_no_compile = (
128 @source_no_preprocess_no_compile_ada,
130 my @source_no_preprocess = (
131 @source_no_preprocess_compile,
132 @source_no_preprocess_no_compile,
134 # List of header file extensions which require preprocessing.
135 my @header_preprocess = (
136 # C, C++, Objective-C, Objective-C++
139 qw( hh H hp hxx hpp HPP h++ tcc ),
143 # Normal object files.
145 # Libtool object files.
147 # Dynamic libraries. bzip2 uses .sho.
153 # Hashes for fast extensions lookup to check if a file falls in one of these
155 my %extensions_no_preprocess = map { $_ => 1 } (
156 # There's no @header_no_preprocess.
157 @source_no_preprocess,
159 my %extensions_preprocess = map { $_ => 1 } (
163 my %extensions_compile_link = map { $_ => 1 } (
165 @source_no_preprocess,
167 my %extensions_compile = map { $_ => 1 } (
168 @source_preprocess_compile,
169 @source_no_preprocess_compile,
171 my %extensions_no_compile = map { $_ => 1 } (
172 @source_preprocess_no_compile,
173 @source_no_preprocess_no_compile,
175 my %extensions_compile_cpp = map { $_ => 1 } (
176 @source_preprocess_compile_cpp,
177 @source_no_preprocess_compile_cpp,
179 my %extensions_ada = map { $_ => 1 } (
180 @source_no_preprocess_compile_ada,
181 @source_no_preprocess_no_compile_ada,
183 my %extensions_fortran = map { $_ => 1 } (
184 @source_no_preprocess_compile_fortran,
185 @source_preprocess_compile_fortran,
187 my %extensions_object = map { $_ => 1 } (
190 my %extension = map { $_ => 1 } (
191 @source_no_preprocess,
197 # Regexp to match file extensions.
198 my $file_extension_regex = qr/
200 \S+ # Filename without extension.
202 ([^\/\\.,;:\s]+)# File extension.
203 (?=\s|\\) # At end of word. Can't use \b because some files have non
204 # word characters at the end and because \b matches double
205 # extensions (like .cpp.o). Works always as all lines are
206 # terminated with "\n".
209 # Expected (hardening) flags. All flags are used as regexps (and compiled to
210 # real regexps below for better execution speed).
213 '-O(?:2|3)', # keep at index 1, search for @def_cflags_debug to change it
215 my @def_cflags_debug = (
216 # These flags indicate a debug build which disables checks for -O2.
220 my @def_cflags_format = (
221 '-Wformat(?:=2)?', # -Wformat=2 implies -Wformat, accept it too
222 '-Werror=format-security', # implies -Wformat-security
224 my @def_cflags_fortify = (
225 # fortify needs at least -O1, but -O2 is recommended anyway
227 my @def_cflags_stack = (
228 '-fstack-protector', # keep first, used by cflags_stack_broken()
229 '--param[= ]ssp-buffer-size=4',
231 my @def_cflags_stack_strong = (
232 '-fstack-protector-strong', # keep first, used by cflags_stack_broken()
234 my @def_cflags_stack_bad = (
235 # Blacklist all stack protector options for simplicity.
236 '-fno-stack-protector',
237 '-fno-stack-protector-all',
238 '-fno-stack-protector-strong',
240 my @def_cflags_stack_clash = (
241 '-fstack-clash-protection',
243 my @def_cflags_pie = (
246 my @def_cflags_branch_amd64 = (
249 my @def_cflags_branch_arm64 = (
250 '-mbranch-protection=standard',
255 # @def_cxxflags_* is the same as @def_cflags_*.
256 my @def_cppflags = ();
257 my @def_cppflags_fortify = (
258 '-D_FORTIFY_SOURCE=[23]', # must be first, see cppflags_fortify_broken()
259 # If you add another flag fix hack below (search for "Hack to fix") and
260 # $def_cppflags_fortify[0].
262 my @def_cppflags_fortify_bad = (
263 # These flags may overwrite -D_FORTIFY_SOURCE=2.
265 '-D_FORTIFY_SOURCE=0',
266 '-D_FORTIFY_SOURCE=1',
268 my @def_ldflags = ();
269 my @def_ldflags_relro = (
272 my @def_ldflags_bindnow = (
275 my @def_ldflags_pie = (
279 my @def_ldflags_pic = (
284 # References to all flags checked by the flag checker.
288 \@def_cflags_fortify,
290 \@def_cflags_stack_strong,
291 \@def_cflags_stack_bad,
292 \@def_cflags_stack_clash,
294 \@def_cflags_branch_amd64,
295 \@def_cflags_branch_arm64,
298 \@def_cppflags_fortify,
301 \@def_ldflags_bindnow,
304 # References to all used flags.
305 my @flag_refs_all = (
308 \@def_cppflags_fortify_bad,
311 # Renaming rules for the output so the regex parts are not visible. Also
312 # stores string values of flag regexps above, see compile_flag_regexp().
315 '-O(?:2|3)' => '-O2',
316 '-Wformat(?:=2)?' => '-Wformat',
317 '--param[= ]ssp-buffer-size=4' => '--param=ssp-buffer-size=4',
318 '-D_FORTIFY_SOURCE=[23]' => '-D_FORTIFY_SOURCE=2',
319 '-Wl,(?:-z,)?relro' => '-Wl,-z,relro',
320 '-Wl,(?:-z,)?now' => '-Wl,-z,now',
324 no_compiler_commands => 1 << 0,
325 # used by POD::Usage => 1 << 1,
326 non_verbose_build => 1 << 2,
327 flags_missing => 1 << 3,
328 hardening_wrapper => 1 << 4,
329 invalid_cmake => 1 << 5,
333 no_compiler_commands => 'I-no-compiler-commands',
334 non_verbose_build => 'W-compiler-flags-hidden',
335 flags_missing => 'W-dpkg-buildflags-missing',
336 hardening_wrapper => 'I-hardening-wrapper-used',
337 invalid_cmake => 'I-invalid-cmake-used',
340 # Statistics of missing flags and non-verbose build commands. Used for
344 preprocess_missing => 0,
346 compile_missing => 0,
348 compile_cpp_missing => 0,
352 commands_nonverbose => 0,
355 # Use colored (ANSI) output?
365 foreach my $delim (';', '&&', '||') {
368 push @x, Text::ParseWords::parse_line(qr/\Q$delim\E/, 1, $_);
374 # Ensure newline at the line end - necessary for
375 # correct parsing later.
382 my ($message, $missing_flags_ref, $flag_renames_ref, $line, $number) = @_;
384 # Get string value of qr//-escaped regexps and if requested rename them.
385 my @missing_flags = map {
386 $flag_renames_ref->{$_}
387 } @{$missing_flags_ref};
389 my $flags = join ' ', @missing_flags;
390 printf '%d:', $number if defined $number;
391 printf '%s (%s)%s %s',
392 error_color($message, 'red'), $flags, error_color(':', 'yellow'),
397 sub error_non_verbose_build {
398 my ($line, $number) = @_;
400 printf '%d:', $number if defined $number;
402 error_color('NONVERBOSE BUILD', 'red'),
403 error_color(':', 'yellow'),
408 sub error_invalid_cmake {
412 error_color('INVALID CMAKE', 'red'),
413 error_color(':', 'yellow'),
418 sub error_hardening_wrapper {
420 error_color('HARDENING WRAPPER', 'red'),
421 error_color(':', 'yellow'),
422 'no checks possible, aborting';
427 my ($message, $color) = @_;
430 return Term::ANSIColor::colored($message, $color);
437 my ($line, @flags) = @_;
439 foreach my $flag (@flags) {
440 return 1 if $line =~ /$flag/;
446 my ($line, $missing_flags_ref, @flags) = @_;
448 my @missing_flags = ();
449 foreach my $flag (@flags) {
450 if (not $line =~ /$flag/) {
451 push @missing_flags, $flag;
455 return 1 if scalar @missing_flags == 0;
457 @{$missing_flags_ref} = @missing_flags;
460 # Check if any of \@bad_flags occurs after $good_flag. Doesn't check if
461 # $good_flag is present.
462 sub flag_overwritten {
463 my ($line, $good_flag, $bad_flags) = @_;
465 if (not any_flags_used($line, @{$bad_flags})) {
470 foreach my $flag (@{$bad_flags}) {
471 while ($line =~ /$flag/g) {
472 if ($bad_pos < $+[0]) {
478 while ($line =~ /$good_flag/g) {
481 if ($good_pos > $bad_pos) {
487 sub cppflags_fortify_broken {
488 my ($line, $missing_flags) = @_;
490 # $def_cppflags_fortify[0] must be -D_FORTIFY_SOURCE=2!
491 my $fortify_source = $def_cppflags_fortify[0];
493 # Some build systems enable/disable fortify source multiple times, check
495 if (not flag_overwritten($line,
497 \@def_cppflags_fortify_bad)) {
500 push @{$missing_flags}, $fortify_source;
504 sub cflags_stack_broken {
505 my ($line, $missing_flags, $strong) = @_;
507 my $flag = $strong ? $def_cflags_stack_strong[0]
508 : $def_cflags_stack[0];
510 if (not flag_overwritten($line, $flag, \@def_cflags_stack_bad)) {
513 push @{$missing_flags}, $flag;
517 # Modifies $missing_flags_ref array.
518 sub pic_pie_conflict {
519 my ($line, $pie, $missing_flags_ref, @flags_pie) = @_;
521 return 0 if not $pie;
522 return 0 if not any_flags_used($line, @def_ldflags_pic);
524 my %flags = map { $_ => 1 } @flags_pie;
526 # Remove all PIE flags from @missing_flags as they are not required with
529 not exists $flags{$_}
530 } @{$missing_flags_ref};
531 @{$missing_flags_ref} = @result;
533 # We got a conflict when no flags are left, thus only PIE flags were
534 # missing. If other flags were missing abort because the conflict is not
536 return scalar @result == 0;
539 sub is_non_verbose_build {
540 my ($line, $cargo, $skip_ref, $input_ref, $line_offset, $line_count) = @_;
542 if ($line =~ /$libtool_regex/o) {
543 # libtool's --silent hides the real compiler flags.
544 if ($line =~ /\s--silent/) {
546 # If --silent is not present, skip this line as some compiler flags
547 # might be missing (e.g. -fPIE) which are handled correctly by libtool
548 # internally. libtool displays the real compiler command on the next
549 # line, so the flags are checked as usual.
556 if (not (index($line, 'checking if you want to see long compiling messages... no') == 0
557 or $line =~ /^\s*\[?(?:CC|CCLD|C\+\+|CXX|CXXLD|LD|LINK)\]?\s+(.+?)$/
558 or $line =~ /^\s*[][\/0-9 ]*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/
559 or $line =~ /^\s*[Bb]uilding (?:program|shared library)\s+(.+?)$/
560 or $line =~ /^\s*\[[\d ]+%\] Building (?:C|CXX) object (.+?)$/)) {
566 # C++ compiler setting.
567 return 0 if $line =~ /^\s*C\+\+.+?:\s+(?:yes|no)\s*$/;
568 return 0 if $line =~ /^\s*C\+\+ Library: stdc\+\+$/;
569 return 0 if $line =~ /^\s*CXX\s*:\s*g\+\+\s*$/;
570 # "Compiling" non binary files.
571 return 0 if $line =~ /^\s*Compiling \S+\.(?:py|pyx|el)['"]?\s*(?:\.\.\.|because it changed\.)?$/;
572 return 0 if $line =~ /^\s*[Cc]ompiling catalog \S+\.po\b/;
574 return 0 if $cargo and $line =~ m{^\s*Compiling\s+\S+\s+v\S+(?:\s+\(/(?:<<PKGBUILDDIR>>|builds/\S+)\))?$};
575 # "Compiling" with no file name.
576 if ($line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/) {
577 # $file_extension_regex may need spaces around the filename.
578 return 0 if not " $1 " =~ /$file_extension_regex/o;
583 # On the first pass we only check if this line is verbose or not.
584 return 1 if not defined $input_ref;
586 # Second pass, we have access to the next lines.
589 # CMake and other build systems print the non-verbose messages also when
590 # building verbose. If a compiler and the file name occurs in the next
591 # lines, treat it as verbose build.
593 # Get filename, we can't use the complete path as only parts of it are
594 # used in the real compiler command.
595 $file =~ m{/([^/\s]+)$};
598 for (my $i = 1; $i <= $line_count; $i++) {
599 my $next_line = $input_ref->[$line_offset + $i];
600 last unless defined $next_line;
602 if (index($next_line, $file) != -1 and $next_line =~ /$cc_regex/o) {
603 # Not a non-verbose line, but we still have to skip the
604 # current line as it doesn't contain any compiler commands.
614 # Remove @flags from $flag_refs_ref, uses $flag_renames_ref as reference.
616 my ($flag_refs_ref, $flag_renames_ref, @flags) = @_;
618 my %removes = map { $_ => 1 } @flags;
619 foreach my $flags (@{$flag_refs_ref}) {
621 # Flag found as string.
622 not exists $removes{$_}
623 # Flag found as string representation of regexp.
624 and (not defined $flag_renames_ref->{$_}
625 or not exists $removes{$flag_renames_ref->{$_}})
632 # Modifies $flag_renames_ref hash.
633 sub compile_flag_regexp {
634 my ($flag_renames_ref, @flags) = @_;
637 foreach my $flag (@flags) {
638 # Compile flag regexp for faster execution.
639 my $regex = qr/\s(['"]?)$flag\1(?:\s|\\)/;
641 # Store flag name in replacement string for correct flags in messages
642 # with qr//ed flag regexps.
643 $flag_renames_ref->{$regex}
644 = (exists $flag_renames_ref->{$flag})
645 ? $flag_renames_ref->{$flag}
648 push @result, $regex;
653 # Does any extension in @extensions exist in %{$extensions_ref}?
654 sub extension_found {
655 my ($extensions_ref, @extensions) = @_;
657 foreach my $extension (@extensions) {
658 if (exists $extensions_ref->{$extension}) {
668 # Parse command line arguments.
670 my $option_version = 0;
672 my $option_bindnow = 0;
673 my @option_ignore_arch = ();
674 my @option_ignore_flag = ();
675 my @option_ignore_arch_flag = ();
676 my @option_ignore_line = ();
677 my @option_ignore_arch_line = ();
679 my $option_arch = undef;
680 my $option_buildd = 0;
681 my $option_debian = 0;
683 my $option_line_numbers = 0;
684 if (not Getopt::Long::GetOptions(
685 'help|h|?' => \$option_help,
686 'version' => \$option_version,
688 'pie' => \$option_pie,
689 'bindnow' => \$option_bindnow,
690 'all' => \$option_all,
692 'ignore-arch=s' => \@option_ignore_arch,
693 'ignore-flag=s' => \@option_ignore_flag,
694 'ignore-arch-flag=s' => \@option_ignore_arch_flag,
695 'ignore-line=s' => \@option_ignore_line,
696 'ignore-arch-line=s' => \@option_ignore_arch_line,
698 'color' => \$option_color,
699 'arch=s' => \$option_arch,
700 'buildd' => \$option_buildd,
701 'debian' => \$option_debian,
702 'line-numbers' => \$option_line_numbers,
705 Pod::Usage::pod2usage(2);
709 Pod::Usage::pod2usage(1);
711 if ($option_version) {
713 blhc $VERSION Copyright (C) 2012-2024 Simon Ruderich
715 This program is free software: you can redistribute it and/or modify
716 it under the terms of the GNU General Public License as published by
717 the Free Software Foundation, either version 3 of the License, or
718 (at your option) any later version.
720 This program is distributed in the hope that it will be useful,
721 but WITHOUT ANY WARRANTY; without even the implied warranty of
722 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
723 GNU General Public License for more details.
725 You should have received a copy of the GNU General Public License
726 along with this program. If not, see <http://www.gnu.org/licenses/>.
732 if (scalar @ARGV == 0) {
734 Pod::Usage::pod2usage(2);
737 # Don't load Term::ANSIColor in buildd mode because Term::ANSIColor is not
738 # installed on Debian's buildds.
739 if (not $option_buildd) {
740 require Term::ANSIColor;
748 # Precompiled ignores for faster lookup.
749 my %option_ignore_arch_flag = ();
750 my %option_ignore_arch_line = ();
752 # Strip flags which should be ignored.
753 if (scalar @option_ignore_flag > 0) {
754 remove_flags(\@flag_refs, \%flag_renames, @option_ignore_flag);
756 # Same for arch specific ignore flags, but only prepare here.
757 if (scalar @option_ignore_arch_flag > 0) {
758 foreach my $ignore (@option_ignore_arch_flag) {
759 my ($ignore_arch, $ignore_flag) = split /:/, $ignore, 2;
761 if (not $ignore_arch or not $ignore_flag) {
762 printf STDERR 'Value "%s" invalid for option ignore-arch-flag '
763 . '("arch:flag" expected)' . "\n", $ignore;
765 Pod::Usage::pod2usage(2);
768 push @{$option_ignore_arch_flag{$ignore_arch}}, $ignore_flag;
772 # Precompile all flag regexps. any_flags_used(), all_flags_used() get a lot
774 foreach my $flags (@flag_refs_all) {
775 @{$flags} = compile_flag_regexp(\%flag_renames, @{$flags});
778 # Precompile ignore line regexps, also anchor at beginning and end of line.
779 # Additional entries are also extracted from the build log, see below.
780 foreach my $ignore (@option_ignore_line) {
781 $ignore = qr/^$ignore$/;
783 # Same for arch specific ignore lines.
784 if (scalar @option_ignore_arch_line > 0) {
785 foreach my $ignore (@option_ignore_arch_line) {
786 my ($ignore_arch, $ignore_line) = split /:/, $ignore, 2;
788 if (not $ignore_arch or not $ignore_line) {
789 printf STDERR 'Value "%s" invalid for option ignore-arch-line '
790 . '("arch:line" expected)' . "\n", $ignore;
792 Pod::Usage::pod2usage(2);
795 push @{$option_ignore_arch_line{$ignore_arch}}, qr/^$ignore_line$/;
803 foreach my $file (@ARGV) {
804 print "checking '$file'...\n" if scalar @ARGV > 1;
806 -f $file or die "No such file: $file";
808 open my $fh, '<', $file or die $!;
810 # Architecture of this file.
811 my $arch = $option_arch;
813 # Hardening options. Not all architectures support all hardening options.
814 my $harden_format = 1;
815 my $harden_fortify = 1;
816 my $harden_stack = 1;
817 my $harden_stack_strong = 1;
818 my $harden_stack_clash = 1;
819 my $harden_branch = 1;
820 my $harden_relro = 1;
821 my $harden_bindnow = $option_bindnow; # defaults to 0
822 my $harden_pie = $option_pie; # defaults to 0
824 # Number of parallel jobs to prevent false positives when detecting
825 # non-verbose builds. As not all jobs declare the number of parallel jobs
826 # use a large enough default.
829 # Don't check for PIE flags if automatically applied by the compiler. Only
830 # used in buildd and Debian mode.
831 my $disable_harden_pie = 0;
832 if ($option_debian) {
833 $disable_harden_pie = 1;
836 # Ignore additional false positives if cargo/rust is used
840 while (my $line = <$fh>) {
843 # Detect architecture automatically unless overridden. For buildd logs
844 # only, doesn't use the dpkg-buildpackage header. Necessary to ignore
845 # build logs which aren't built (wrong architecture, build error,
848 if (index($line, 'Build Architecture: ') == 0) {
849 $arch = substr $line, 20, -1; # -1 to ignore '\n' at the end
850 # For old logs (sbuild << 0.63.0-1).
851 } elsif (index($line, 'Architecture: ') == 0) {
852 $arch = substr $line, 14, -1; # -1 to ignore '\n' at the end
856 # dpkg-buildflags only provides hardening flags since 1.16.1, don't
857 # check for hardening flags in buildd mode if an older dpkg-dev is
858 # used. Default flags (-g -O2) are still checked.
860 # Packages which were built before 1.16.1 but used their own hardening
861 # flags are not checked.
863 # Strong stack protector is used since dpkg 1.17.11.
865 # Recent GCC versions automatically use PIE (only on supported
866 # architectures) and dpkg respects this properly since 1.18.15 and
867 # doesn't pass PIE flags manually.
869 and index($line, 'Toolchain package versions: ') == 0) {
870 require Dpkg::Version;
873 my $disable_strong = 1;
874 my $disable_clash = 1;
875 my $disable_branch = 1;
877 if ($line =~ /\bdpkg-dev_(\S+)/) {
878 if (Dpkg::Version::version_compare($1, '1.16.1') >= 0) {
881 if (Dpkg::Version::version_compare($1, '1.17.11') >= 0) {
884 if (Dpkg::Version::version_compare($1, '1.18.15') >= 0) {
885 $disable_harden_pie = 1;
887 if (Dpkg::Version::version_compare($1, '1.22.0') >= 0) {
901 if ($disable_strong) {
902 $harden_stack_strong = 0;
904 if ($disable_clash) {
905 $harden_stack_clash = 0;
907 if ($disable_branch) {
912 # The following two versions of CMake in Debian obeyed CPPFLAGS, but
913 # this was later dropped because upstream rejected the patch. Thus
914 # build logs with these versions will have fortify hardening flags
915 # enabled, even though they may be not correctly set and are missing
916 # when build with later CMake versions. Thanks to Aron Xu for letting
918 if (index($line, 'Package versions: ') == 0
919 and $line =~ /\bcmake_(\S+)/
920 and ($1 eq '2.8.7-1' or $1 eq '2.8.7-2')) {
921 if (not $option_buildd) {
922 error_invalid_cmake($1);
923 $exit |= $exit_code{invalid_cmake};
925 print "$buildd_tag{invalid_cmake}|$1|\n";
929 # Debian's build daemons use "Filtered Build-Depends:" (or just
930 # "Build-Depends:" in older versions) for the build dependencies, but
931 # pbuilder uses "Depends:"; support both.
932 if (index($line, 'Filtered Build-Depends: ') == 0
933 or index($line, 'Build-Depends: ') == 0
934 or index($line, 'Depends: ') == 0) {
935 # If hardening wrapper is used (wraps calls to gcc and adds
936 # hardening flags automatically) we can't perform any checks,
938 if ($line =~ /\bhardening-wrapper\b/) {
939 if (not $option_buildd) {
940 error_hardening_wrapper();
941 $exit |= $exit_code{hardening_wrapper};
943 print "$buildd_tag{hardening_wrapper}||\n";
948 if ($line =~ /\bcargo\b/) {
953 # This flags is not always available, but if it is use it.
954 if ($line =~ /^DEB_BUILD_OPTIONS=.*\bparallel=(\d+)/) {
958 # We skip over unimportant lines at the beginning of the log to
959 # prevent false positives.
960 last if index($line, 'dpkg-buildpackage: ') == 0;
963 # Input lines, contain only the lines with compiler commands.
965 # Non-verbose lines in the input. Used to reduce calls to
966 # is_non_verbose_build() (which is quite slow) in the second loop when
967 # it's already clear if a line is non-verbose or not.
968 my @input_nonverbose = ();
970 my @input_number = ();
972 my $continuation = 0;
973 my $complete_line = undef;
975 while (my $line = <$fh>) {
978 # And stop at the end of the build log. Package details (reported by
979 # the buildd logs) are not important for us. This also prevents false
981 last if index($line, 'Build finished at ') == 0
982 and $line =~ /^Build finished at \d{8}-\d{4}$/;
984 if (not $continuation) {
988 # Detect architecture automatically unless overridden.
990 and index($line, 'dpkg-buildpackage: info: host architecture ') == 0) {
991 $arch = substr $line, 43, -1; # -1 to ignore '\n' at the end
992 # Older versions of dpkg-buildpackage
994 and index($line, 'dpkg-buildpackage: host architecture ') == 0) {
995 $arch = substr $line, 37, -1; # -1 to ignore '\n' at the end
997 # Old buildd logs use e.g. "host architecture is alpha", remove
998 # the "is", otherwise debarch_to_debtriplet() will not detect the
1000 if (index($arch, 'is ') == 0) {
1001 $arch = substr $arch, 3;
1005 # Permit dynamic excludes from within the build log to ignore false
1006 # positives. Cannot use a separate config file as we often only have
1007 # the build log itself.
1008 if (index($line, 'blhc: ignore-line-regexp: ') == 0) {
1009 my $ignore = substr $line, 26, -1; # -1 to ignore '\n' at the end
1010 push @option_ignore_line, qr/^$ignore$/;
1014 next if $line =~ /^\s*#/;
1015 # Ignore compiler warnings for now.
1016 next if $line =~ /$warning_regex/o;
1018 if (not $option_buildd and index($line, "\033") != -1) { # \033 = esc
1019 # Remove all ANSI color sequences which are sometimes used in
1020 # non-verbose builds.
1021 $line = Term::ANSIColor::colorstrip($line);
1022 # Also strip '\0xf' (delete previous character), used by Elinks'
1025 # And "ESC(B" which seems to be used on armhf and hurd (not sure
1027 $line =~ s/\033\(B//g;
1030 # Check if this line indicates a non verbose build.
1032 $non_verbose |= is_non_verbose_build($line, $cargo, \$skip);
1035 # Treat each command as a single line so we don't ignore valid
1036 # commands when handling false positives. split_line() is slow, only
1037 # use it when necessary.
1038 my @line = ($line !~ /(?:;|&&|\|\|)/)
1040 : split_line($line);
1041 foreach my $line (@line) {
1042 if ($continuation) {
1045 # Join lines, but leave the "\" in place so it's clear where
1046 # the original line break was.
1047 chomp $complete_line;
1048 $complete_line .= ' ' . $line;
1050 # Line continuation, line ends with "\".
1051 if ($line =~ /\\$/) {
1053 # Start line continuation.
1054 if (not defined $complete_line) {
1055 $complete_line = $line;
1060 # Use the complete line if a line continuation occurred.
1061 if (defined $complete_line) {
1062 $line = $complete_line;
1063 $complete_line = undef;
1067 # Strip (basic) environment variables for compiler detection. This
1068 # prevents false positives when environment variables contain
1069 # compiler binaries. Nested quotes, command substitution, etc. is
1074 [a-zA-Z_]+ # environment variable name
1077 [^\s"'\$`\\]+ # non-quoted string
1079 '[^"'\$`\\]*' # single-quoted string
1081 "[^"'\$`\\]*" # double-quoted string
1086 # Ignore lines with no compiler commands.
1087 next if not $non_verbose
1088 and not $noenv =~ /$cc_regex_normal/o;
1089 # Ignore lines with no filenames with extensions. May miss some
1090 # non-verbose builds (e.g. "gcc -o test" [sic!]), but shouldn't be
1091 # a problem as the log will most likely contain other non-verbose
1092 # commands which are detected.
1093 next if not $non_verbose
1094 and not $line =~ /$file_extension_regex/o;
1096 # Ignore false positives.
1098 # `./configure` output.
1099 next if not $non_verbose
1100 and $line =~ /^(?:checking|[Cc]onfigure:) /;
1101 next if $line =~ /^\s*(?:Host\s+)?(?:C(?:\+\+)?\s+)?
1102 [Cc]ompiler[\s.]*:?\s+
1104 next if $line =~ m{^\s*(?:-\s)?(?:HOST_)?(?:CC|CXX)
1105 \s*=\s*$cc_regex_full
1106 # optional compiler options, don't allow
1107 # "everything" here to prevent false negatives
1108 \s*(?:\s-\S+)*\s*$}xo;
1109 # `echo` is never a compiler command
1110 next if $line =~ /^\s*echo\s/;
1111 # Ignore calls to `make` because they can contain environment
1112 # variables which look like compiler commands, e.g. CC=).
1113 next if $line =~ /^\s*make\s/;
1114 # `moc-qt4`/`moc-qt5` contain '-I.../linux-g++' in their command
1115 # line (or similar for other architectures) which gets recognized
1116 # as a compiler line, but `moc-qt*` is only a preprocessor for Qt
1117 # C++ files. No hardening flags are relevant during this step,
1118 # thus ignore `moc-qt*` lines. The resulting files will be
1119 # compiled in a separate step (and therefore checked).
1120 next if $line =~ m{^\S+(?:/bin/moc(?:-qt[45])?|/lib/qt6/libexec/moc)
1122 -I\S+/mkspecs/[a-z]+-g\++(?:-64)?
1124 # nvcc is not a regular C compiler
1125 next if $line =~ m{^\S+/bin/nvcc\s};
1126 # Ignore false positives when the line contains only CC=gcc but no
1127 # other gcc command.
1128 if ($line =~ /(.*)CC=$cc_regex_full(.*)/o) {
1131 next if not $before =~ /$cc_regex_normal/o
1132 and not $after =~ /$cc_regex_normal/o;
1134 # Ignore false positives caused by gcc -v. It outputs a line
1135 # looking like a normal compiler line but which is sometimes
1136 # missing hardening flags, although the normal compiler line
1138 next if $line =~ m{^\s+/usr/lib/gcc/$cc_regex_full_prefix/
1139 [0-9.]+/cc1(?:plus)?}xo;
1140 # Ignore false positive with `rm` which may remove files which
1141 # look like a compiler executable thus causing the line to be
1142 # treated as a normal compiler line.
1143 next if $line =~ m{^\s*rm\s+};
1144 next if $line =~ m{^\s*dwz\s+};
1145 # Some build systems emit "gcc > file".
1146 next if $line =~ m{$cc_regex_normal\s*>\s*\S+}o;
1147 # Hex output may contain "cc".
1148 next if $line =~ m#(?:\b[0-9a-fA-F]{2,}\b\s*){5}#;
1149 # Meson build output
1150 next if $line =~ /^C\+\+ linker for the host machine: /;
1151 # Embedded `gcc -print-*` commands
1152 next if $line =~ /`$cc_regex_normal\s*[^`]*-print-\S+`/;
1153 # cmake checking for compiler flags without setting CPPFLAGS
1154 next if $line =~ m{^\s*/usr/(bin|lib)/(ccache/)?c\+\+ (?:-std=\S+ )?-dM -E -c /usr/share/cmake-\S+/Modules/CMakeCXXCompilerABI\.cpp};
1155 # Some rustc lines look like linker commands
1156 next if $cargo and $line =~ /$rustc_regex/o;
1158 # Check if additional hardening options were used. Used to ensure
1159 # they are used for the complete build.
1160 $harden_pie = 1 if any_flags_used($line, @def_cflags_pie,
1162 $harden_bindnow = 1 if any_flags_used($line, @def_ldflags_bindnow);
1165 push @input_nonverbose, $non_verbose;
1166 push @input_number, $number if $option_line_numbers;
1170 close $fh or die $!;
1172 # Ignore arch if requested.
1173 if (scalar @option_ignore_arch > 0 and $arch) {
1174 foreach my $ignore (@option_ignore_arch) {
1175 if ($arch eq $ignore) {
1176 print "ignoring architecture '$arch'\n";
1182 if (scalar @input == 0) {
1184 if (not $option_buildd) {
1185 print "No compiler commands!\n";
1186 $exit |= $exit_code{no_compiler_commands};
1188 print "$buildd_tag{no_compiler_commands}||\n";
1194 if ($option_buildd) {
1195 $statistics{commands} += scalar @input;
1198 # Option or auto detected.
1199 my @harden_branch_flags;
1201 # The following was partially copied from dpkg-dev 1.22.0
1202 # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, set_build_features and
1203 # _add_build_flags()), copyright Raphaël Hertzog <hertzog@debian.org>,
1204 # Guillem Jover <guillem@debian.org>, Kees Cook <kees@debian.org>,
1205 # Canonical, Ltd. licensed under GPL version 2 or later. Keep it in
1210 # Recent dpkg versions use a quadruplet for arch. Support both.
1212 (undef, undef, $os, $cpu) = Dpkg::Arch::debarch_to_debtuple($arch);
1215 (undef, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch);
1218 my %builtin_pie_arch = map { $_ => 1 } qw(
1250 # Disable unsupported hardening options.
1251 if ($disable_harden_pie and exists $builtin_pie_arch{$arch}) {
1254 if ($os !~ /^(?:linux|kfreebsd|hurd)$/
1255 or $cpu =~ /^(?:alpha|hppa|ia64)$/) {
1258 if ($cpu =~ /^(?:ia64|alpha|hppa|nios2)$/ or $arch eq 'arm') {
1260 $harden_stack_strong = 0;
1262 if ($arch !~ /^(?:amd64|arm64|armhf|armel)$/) {
1263 $harden_stack_clash = 0;
1265 if ($cpu =~ /^(?:ia64|hppa)$/) {
1267 $harden_bindnow = 0;
1269 if ($cpu eq 'amd64') {
1270 @harden_branch_flags = @def_cflags_branch_amd64;
1271 } elsif ($cpu eq 'arm64') {
1272 @harden_branch_flags = @def_cflags_branch_arm64;
1277 my @cflags = @def_cflags;
1278 my @cxxflags = @def_cxxflags;
1279 my @cppflags = @def_cppflags;
1280 my @ldflags = @def_ldflags;
1281 # Check the specified hardening options, same order as dpkg-buildflags.
1283 @cflags = (@cflags, @def_cflags_pie);
1284 @cxxflags = (@cxxflags, @def_cflags_pie);
1285 @ldflags = (@ldflags, @def_ldflags_pie);
1287 if ($harden_stack_strong) {
1288 @cflags = (@cflags, @def_cflags_stack_strong);
1289 @cxxflags = (@cxxflags, @def_cflags_stack_strong);
1290 } elsif ($harden_stack) {
1291 @cflags = (@cflags, @def_cflags_stack);
1292 @cxxflags = (@cxxflags, @def_cflags_stack);
1294 if ($harden_stack_clash) {
1295 @cflags = (@cflags, @def_cflags_stack_clash);
1296 @cxxflags = (@cxxflags, @def_cflags_stack_clash);
1298 if ($harden_fortify) {
1299 @cflags = (@cflags, @def_cflags_fortify);
1300 @cxxflags = (@cxxflags, @def_cflags_fortify);
1301 @cppflags = (@cppflags, @def_cppflags_fortify);
1303 if ($harden_format) {
1304 @cflags = (@cflags, @def_cflags_format);
1305 @cxxflags = (@cxxflags, @def_cflags_format);
1307 if ($harden_branch and @harden_branch_flags) {
1308 @cflags = (@cflags, @harden_branch_flags);
1309 @cxxflags = (@cxxflags, @harden_branch_flags);
1311 if ($harden_relro) {
1312 @ldflags = (@ldflags, @def_ldflags_relro);
1314 if ($harden_bindnow) {
1315 @ldflags = (@ldflags, @def_ldflags_bindnow);
1318 # Ada doesn't support format hardening flags, see #680117 for more
1319 # information. Same for fortran.
1321 my @cflags_noformat = grep {
1323 foreach my $flag (@def_cflags_format) {
1324 $ok = 0 if $_ eq $flag;
1329 # Hack to fix cppflags_fortify_broken() if --ignore-flag
1330 # -D_FORTIFY_SOURCE=2 is used to ignore missing fortification. Only works
1331 # as long as @def_cppflags_fortify contains only one variable.
1332 if (scalar @def_cppflags_fortify == 0) {
1333 $harden_fortify = 0;
1336 # Ignore flags for this arch if requested.
1337 if ($arch and exists $option_ignore_arch_flag{$arch}) {
1338 my @local_flag_refs = (\@cflags, \@cxxflags, \@cppflags, \@ldflags);
1340 remove_flags(\@local_flag_refs,
1342 @{$option_ignore_arch_flag{$arch}});
1345 my @ignore_line = @option_ignore_line;
1346 # Ignore lines for this arch if requested.
1347 if ($arch and exists $option_ignore_arch_line{$arch}) {
1348 @ignore_line = (@ignore_line, @{$option_ignore_arch_line{$arch}});
1352 for (my $i = 0; $i < scalar @input; $i++) {
1353 my $line = $input[$i];
1355 # Ignore line if requested.
1356 foreach my $ignore (@ignore_line) {
1357 next LINE if $line =~ /$ignore/;
1361 if ($input_nonverbose[$i]
1362 and is_non_verbose_build($line, $cargo, \$skip,
1363 \@input, $i, $parallel)) {
1364 if (not $option_buildd) {
1365 error_non_verbose_build($line, $input_number[$i]);
1366 $exit |= $exit_code{non_verbose_build};
1368 $statistics{commands_nonverbose}++;
1372 # Even if it's a verbose build, we might have to skip this line (see
1373 # is_non_verbose_build()).
1376 my $orig_line = $line;
1378 # Remove everything until and including the compiler command. Makes
1379 # checks easier and faster.
1380 $line =~ s/^.*?$cc_regex//o;
1381 # "([...] test.c)" is not detected as 'test.c' - fix this by removing
1382 # the brace and similar characters at the line end.
1383 $line =~ s/['")]+$//;
1385 # Skip unnecessary tests when only preprocessing.
1386 my $flag_preprocess = 0;
1393 # Preprocess, compile, assemble.
1394 if ($line =~ /\s(-E|-S|-c)\b/) {
1396 $flag_preprocess = 1 if $1 eq '-E';
1397 $compile = 1 if $1 eq '-S' or $1 eq '-c';
1398 # Dependency generation for Makefiles. The other flags (-MF -MG -MP
1399 # -MT -MQ) are always used with -M/-MM.
1400 } elsif ($line =~ /\s(?:-M|-MM)\b/) {
1402 # Otherwise assume we are linking.
1407 # -MD/-MMD also cause dependency generation, but they don't imply -E!
1408 if ($line =~ /\s(?:-MD|-MMD)\b/) {
1410 $flag_preprocess = 0;
1413 # Dependency generation for Makefiles, no preprocessing or other flags
1415 next if $dependency;
1417 # Get all file extensions on this line.
1418 my @extensions = $line =~ /$file_extension_regex/go;
1419 # Ignore all unknown extensions to speedup the search below.
1420 @extensions = grep { exists $extension{$_} } @extensions;
1422 # These file types don't require preprocessing.
1423 if (extension_found(\%extensions_no_preprocess, @extensions)) {
1426 # These file types require preprocessing.
1427 if (extension_found(\%extensions_preprocess, @extensions)) {
1428 # Prevent false positives with "libtool: link: g++ -include test.h
1429 # .." compiler lines.
1430 if ($orig_line !~ /$libtool_link_regex/o) {
1435 if (not $flag_preprocess) {
1436 # If there are source files then it's compiling/linking in one
1437 # step and we must check both. We only check for source files
1438 # here, because header files cause too many false positives.
1439 if (extension_found(\%extensions_compile_link, @extensions)) {
1440 # Assembly files don't need CFLAGS.
1441 if (not extension_found(\%extensions_compile, @extensions)
1442 and extension_found(\%extensions_no_compile, @extensions)) {
1444 # But the rest does.
1448 # No compilable extensions found, either linking or compiling
1451 # If there are also no object files we are just compiling headers
1452 # (.h -> .h.gch). Don't check for linker flags in this case. Due
1453 # to our liberal checks for compiler lines, this also reduces the
1454 # number of false positives considerably.
1456 and not extension_found(\%extensions_object, @extensions)) {
1461 my $compile_cpp = 0;
1462 my $restore_cflags = 0;
1463 # Assume CXXFLAGS are required when a C++ file is specified in the
1466 and extension_found(\%extensions_compile_cpp, @extensions)) {
1469 # Ada needs special CFLAGS
1470 } elsif (extension_found(\%extensions_ada, @extensions)) {
1471 $restore_cflags = 1;
1472 $preprocess = 0; # Ada uses no CPPFLAGS
1473 @cflags_backup = @cflags;
1474 @cflags = @cflags_noformat;
1476 } elsif (extension_found(\%extensions_fortran, @extensions)) {
1477 $restore_cflags = 1;
1478 @cflags_backup = @cflags;
1479 @cflags = @cflags_noformat;
1482 if ($option_buildd) {
1483 $statistics{preprocess}++ if $preprocess;
1484 $statistics{compile}++ if $compile;
1485 $statistics{compile_cpp}++ if $compile_cpp;
1486 $statistics{link}++ if $link;
1489 # Check if there are flags indicating a debug build. If that's true,
1490 # skip the check for -O2. This prevents fortification, but that's fine
1491 # for a debug build.
1492 if (any_flags_used($line, @def_cflags_debug)) {
1493 remove_flags([\@cflags], \%flag_renames, $def_cflags[1]);
1494 remove_flags([\@cppflags], \%flag_renames, $def_cppflags_fortify[0]);
1497 # Check hardening flags.
1499 if ($compile and (not all_flags_used($line, \@missing, @cflags)
1500 or (($harden_stack or $harden_stack_strong)
1501 and cflags_stack_broken($line, \@missing,
1502 $harden_stack_strong)))
1503 # Libraries linked with -fPIC don't have to (and can't) be
1504 # linked with -fPIE as well. It's no error if only PIE flags
1506 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1507 # Assume dpkg-buildflags returns the correct flags.
1508 and index($line, '`dpkg-buildflags --get CFLAGS`') == -1) {
1509 if (not $option_buildd) {
1510 error_flags('CFLAGS missing', \@missing, \%flag_renames,
1511 $input[$i], $input_number[$i]);
1512 $exit |= $exit_code{flags_missing};
1514 $statistics{compile_missing}++;
1516 } elsif ($compile_cpp and not all_flags_used($line, \@missing, @cflags)
1517 # Libraries linked with -fPIC don't have to (and can't) be
1518 # linked with -fPIE as well. It's no error if only PIE flags
1520 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1521 # Assume dpkg-buildflags returns the correct flags.
1522 and index($line, '`dpkg-buildflags --get CXXFLAGS`') == -1) {
1523 if (not $option_buildd) {
1524 error_flags('CXXFLAGS missing', \@missing, \%flag_renames,
1525 $input[$i], $input_number[$i]);
1526 $exit |= $exit_code{flags_missing};
1528 $statistics{compile_cpp_missing}++;
1532 and (not all_flags_used($line, \@missing, @cppflags)
1533 # The fortify flag might be overwritten, detect that.
1535 and cppflags_fortify_broken($line, \@missing)))
1536 # Assume dpkg-buildflags returns the correct flags.
1537 and index($line, '`dpkg-buildflags --get CPPFLAGS`') == -1) {
1538 if (not $option_buildd) {
1539 error_flags('CPPFLAGS missing', \@missing, \%flag_renames,
1540 $input[$i], $input_number[$i]);
1541 $exit |= $exit_code{flags_missing};
1543 $statistics{preprocess_missing}++;
1546 if ($link and not all_flags_used($line, \@missing, @ldflags)
1547 # Same here, -fPIC conflicts with -fPIE.
1548 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_ldflags_pie)
1549 # Assume dpkg-buildflags returns the correct flags.
1550 and index($line, '`dpkg-buildflags --get LDFLAGS`') == -1) {
1551 if (not $option_buildd) {
1552 error_flags('LDFLAGS missing', \@missing, \%flag_renames,
1553 $input[$i], $input_number[$i]);
1554 $exit |= $exit_code{flags_missing};
1556 $statistics{link_missing}++;
1560 # Restore normal CFLAGS.
1561 if ($restore_cflags) {
1562 @cflags = @cflags_backup;
1567 # Print statistics for buildd mode, only output in this mode.
1568 if ($option_buildd) {
1571 if ($statistics{preprocess_missing}) {
1572 push @warning, sprintf 'CPPFLAGS %d (of %d)',
1573 $statistics{preprocess_missing},
1574 $statistics{preprocess};
1576 if ($statistics{compile_missing}) {
1577 push @warning, sprintf 'CFLAGS %d (of %d)',
1578 $statistics{compile_missing},
1579 $statistics{compile};
1581 if ($statistics{compile_cpp_missing}) {
1582 push @warning, sprintf 'CXXFLAGS %d (of %d)',
1583 $statistics{compile_cpp_missing},
1584 $statistics{compile_cpp};
1586 if ($statistics{link_missing}) {
1587 push @warning, sprintf 'LDFLAGS %d (of %d)',
1588 $statistics{link_missing},
1591 if (scalar @warning) {
1592 local $" = ', '; # array join string
1593 print "$buildd_tag{flags_missing}|@warning missing|\n";
1596 if ($statistics{commands_nonverbose}) {
1597 printf "$buildd_tag{non_verbose_build}|%d (of %d) hidden|\n",
1598 $statistics{commands_nonverbose},
1599 $statistics{commands},
1611 blhc - build log hardening check, checks build logs for missing hardening flags
1615 B<blhc> [I<options>] I<< <dpkg-buildpackage build log file>.. >>
1619 blhc is a small tool which checks build logs for missing hardening flags. It's
1620 licensed under the GPL 3 or later.
1622 It's designed to check build logs generated by Debian's dpkg-buildpackage (or
1623 tools using dpkg-buildpackage like pbuilder or sbuild (which is used for the
1624 official buildd build logs)) to help maintainers detect missing hardening
1625 flags in their packages.
1627 Only gcc is detected as compiler at the moment. If other compilers support
1628 hardening flags as well, please report them.
1630 If there's no output, no flags are missing and the build log is fine.
1632 See F<README> for details about performed checks, auto-detection and
1635 =head1 FALSE POSITIVES
1637 To suppress false positives you can embed the following string in the build
1640 blhc: ignore-line-regexp: REGEXP
1642 All lines fully matching REGEXP (see B<--ignore-line> for details) will be
1643 ignored. The string can be embedded multiple times to ignore different
1646 Please use this feature sparingly so that missing flags are not overlooked. If
1647 you find false positives which affect more packages please report a bug.
1649 To generate this string simply use echo in C<debian/rules>; make sure to use @
1650 to suppress the echo command itself as it could also trigger a false positive.
1651 If the build process takes a long time edit the C<.build> file in place and
1652 tweak the ignore string until B<blhc --all --debian package.build> no longer
1653 reports any false positives.
1661 Force check for all +all (+pie, +bindnow) hardening flags. By default it's
1664 =item B<--arch> I<architecture>
1666 Set the specific architecture (e.g. amd64, armel, etc.), automatically
1667 disables hardening flags not available on this architecture. Is detected
1668 automatically if dpkg-buildpackage is used.
1672 Force check for all +bindnow hardening flags. By default it's auto detected.
1676 Special mode for buildds when automatically parsing log files. The following
1677 changes are in effect:
1683 Print tags instead of normal warnings, see L</"BUILDD TAGS"> for a list of
1688 Don't check hardening flags in old log files (if dpkg-dev << 1.16.1 is
1693 Don't require Term::ANSIColor.
1697 Return exit code 0, unless there was a error (-I, -W messages don't count as
1704 Apply Debian-specific settings. At the moment this only disables checking for
1705 PIE which is automatically applied by Debian's GCC and no longer requires a
1706 compiler command line argument.
1710 Use colored (ANSI) output for warning messages.
1712 =item B<--line-numbers>
1714 Display line numbers.
1716 =item B<--ignore-arch> I<arch>
1718 Ignore build logs from architectures matching I<arch>. I<arch> is a string.
1720 Used to prevent false positives. This option can be specified multiple times.
1722 =item B<--ignore-arch-flag> I<arch>:I<flag>
1724 Like B<--ignore-flag>, but only ignore flag on I<arch>.
1726 =item B<--ignore-arch-line> I<arch>:I<line>
1728 Like B<--ignore-line>, but only ignore line on I<arch>.
1730 =item B<--ignore-flag> I<flag>
1732 Don't print an error when the specific flag is missing in a compiler line.
1733 I<flag> is a string.
1735 Used to prevent false positives. This option can be specified multiple times.
1737 =item B<--ignore-line> I<regex>
1739 Ignore lines matching the given Perl regex. I<regex> is automatically anchored
1740 at the beginning and end of the line to prevent false negatives.
1742 B<NOTE>: Not the input lines are checked, but the lines which are displayed in
1743 warnings (which have line continuation resolved).
1745 Used to prevent false positives. This option can be specified multiple times.
1749 Force check for all +pie hardening flags. By default it's auto detected.
1751 =item B<-h -? --help>
1753 Print available options.
1757 Print version number and license.
1761 Auto detection for B<--pie> and B<--bindnow> only works if at least one
1762 command uses the required hardening flag (e.g. -fPIE). Then it's required for
1763 all other commands as well.
1767 Normal usage, parse a single log file.
1769 blhc path/to/log/file
1771 If there's no output, no flags are missing and the build log is fine.
1773 Parse multiple log files. The exit code is ORed over all files.
1775 blhc path/to/directory/with/log/files/*
1777 Don't treat missing C<-g> as error:
1779 blhc --ignore-flag -g path/to/log/file
1781 Don't treat missing C<-pie> on kfreebsd-amd64 as error:
1783 blhc --ignore-arch-flag kfreebsd-amd64:-pie path/to/log/file
1785 Ignore lines consisting exactly of C<./script gcc file> which would cause a
1788 blhc --ignore-line '\./script gcc file' path/to/log/file
1790 Ignore lines matching C<./script gcc file> somewhere in the line.
1792 blhc --ignore-line '.*\./script gcc file.*' path/to/log/file
1794 Use blhc with pbuilder.
1796 pbuilder path/to/package.dsc | tee path/log/file
1797 blhc path/to/file || echo flags missing
1799 Assume this build log was created on a Debian system and thus don't warn about
1800 missing PIE flags if the current architecture injects them automatically (this
1801 is enabled in buildd mode per default). C<--arch> is necessary if the build
1802 log contains no architecture information as written by dpkg-buildpackage.
1804 blhc --debian --all --arch=amd64 path/to/log/file
1808 The following tags are used in I<--buildd> mode. In braces the additional data
1813 =item B<I-hardening-wrapper-used>
1815 The package uses hardening-wrapper which intercepts calls to gcc and adds
1816 hardening flags. The build log doesn't contain any hardening flags and thus
1817 can't be checked by blhc.
1819 =item B<W-compiler-flags-hidden> (summary of hidden lines)
1821 Build log contains lines which hide the real compiler flags. For example:
1828 Most of the time either C<export V=1> or C<export verbose=1> in
1829 F<debian/rules> fixes builds with hidden compiler flags. Sometimes C<.SILENT>
1830 in a F<Makefile> must be removed. And as last resort the F<Makefile> must be
1831 patched to remove the C<@>s hiding the real compiler commands.
1833 =item B<W-dpkg-buildflags-missing> (summary of missing flags)
1835 CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS missing.
1837 =item B<I-invalid-cmake-used> (version)
1839 By default CMake ignores CPPFLAGS thus missing those hardening flags. Debian
1840 patched CMake in versions 2.8.7-1 and 2.8.7-2 to respect CPPFLAGS, but this
1841 patch was rejected by upstream and later reverted in Debian. Thus those two
1842 versions show correct usage of CPPFLAGS even if the package doesn't correctly
1843 handle them (for example by passing them to CFLAGS). To prevent false
1844 negatives just blacklist those two versions.
1846 =item B<I-no-compiler-commands>
1848 No compiler commands were detected. Either the log contains none or they were
1849 not correctly detected by blhc (please report the bug in this case).
1855 The exit status is a "bit mask", each listed status is ORed when the error
1856 condition occurs to get the result.
1866 No compiler commands were found.
1870 Invalid arguments/options given to blhc.
1878 Missing hardening flags.
1882 Hardening wrapper detected, no tests performed.
1886 Invalid CMake version used. See B<I-invalid-cmake-used> under L</"BUILDD
1887 TAGS"> for a detailed explanation.
1893 Simon Ruderich, E<lt>simon@ruderich.orgE<gt>
1895 Thanks to to Bernhard R. Link E<lt>brlink@debian.orgE<gt> and Jaria Alto
1896 E<lt>jari.aalto@cante.netE<gt> for their valuable input and suggestions.
1898 =head1 LICENSE AND COPYRIGHT
1900 Copyright (C) 2012-2024 by Simon Ruderich
1902 This program is free software: you can redistribute it and/or modify
1903 it under the terms of the GNU General Public License as published by
1904 the Free Software Foundation, either version 3 of the License, or
1905 (at your option) any later version.
1907 This program is distributed in the hope that it will be useful,
1908 but WITHOUT ANY WARRANTY; without even the implied warranty of
1909 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1910 GNU General Public License for more details.
1912 You should have received a copy of the GNU General Public License
1913 along with this program. If not, see <http://www.gnu.org/licenses/>.
1917 L<hardening-check(1)>, L<dpkg-buildflags(1)>