3 # Build log hardening check, checks build logs for missing hardening flags.
5 # Copyright (C) 2012 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.02';
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\+\+)
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 = qr/
42 (?:[a-z0-9_]+-(?:linux-|kfreebsd-)?gnu(?:eabi|eabihf)?-)?
45 # Regex to catch (GCC) compiler warnings.
46 my $warning_regex = qr/^(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]$/;
48 # List of source file extensions which require preprocessing.
49 my @source_preprocess_compile_cpp = (
51 qw( cc cp cxx cpp CPP c++ C ),
55 my @source_preprocess_compile = (
61 @source_preprocess_compile_cpp,
63 qw( F FOR fpp FPP FTN F90 F95 F03 F08 ),
65 my @source_preprocess_no_compile = (
69 my @source_preprocess = (
70 @source_preprocess_compile,
71 @source_preprocess_no_compile,
73 # List of source file extensions which don't require preprocessing.
74 my @source_no_preprocess_compile_cpp = (
80 my @source_no_preprocess_compile = (
84 @source_no_preprocess_compile_cpp,
88 qw( f for ftn f90 f95 f03 f08 ),
90 my @source_no_preprocess_no_compile = (
94 my @source_no_preprocess = (
95 @source_no_preprocess_compile,
96 @source_no_preprocess_no_compile,
98 # List of header file extensions which require preprocessing.
99 my @header_preprocess = (
100 # C, C++, Objective-C, Objective-C++
103 qw( hh H hp hxx hpp HPP h++ tcc ),
106 # Hashes for fast extensions lookup to check if a file falls in one of these
108 my %extensions_no_preprocess = map { $_ => 1 } (
109 @source_no_preprocess,
111 my %extensions_preprocess = map { $_ => 1 } (
115 my %extensions_compile_link = map { $_ => 1 } (
117 @source_no_preprocess,
119 my %extensions_compile = map { $_ => 1 } (
120 @source_preprocess_compile,
121 @source_no_preprocess_compile,
123 my %extensions_no_compile = map { $_ => 1 } (
124 @source_preprocess_no_compile,
125 @source_no_preprocess_no_compile,
127 my %extensions_compile_cpp = map { $_ => 1 } (
128 @source_preprocess_compile_cpp,
129 @source_no_preprocess_compile_cpp,
131 my %extension = map { $_ => 1 } (
132 @source_no_preprocess,
137 # Regexp to match file extensions.
138 my $file_extension_regex = qr/
140 \S+ # Filename without extension.
142 ([^\/\\.,;:\s]+)# File extension.
143 (?=\s|\\) # At end of word. Can't use \b because some files have non
144 # word characters at the end and because \b matches double
145 # extensions (like .cpp.o). Works always as all lines are
146 # terminated with "\n".
149 # Expected (hardening) flags. All flags are used as regexps.
154 my @def_cflags_format = (
156 '-Werror=format-security', # implies -Wformat-security
158 my @def_cflags_fortify = (
159 # fortify needs at least -O1, but -O2 is recommended anyway
161 my @def_cflags_stack = (
163 '--param=ssp-buffer-size=4',
165 my @def_cflags_pie = (
171 # @def_cxxflags_* is the same as @def_cflags_*.
172 my @def_cppflags = ();
173 my @def_cppflags_fortify = (
174 '-D_FORTIFY_SOURCE=2', # must be first, see cppflags_fortify_broken()
176 my @def_cppflags_fortify_bad = (
177 # These flags may overwrite -D_FORTIFY_SOURCE=2.
178 '-D_FORTIFY_SOURCE=0',
179 '-D_FORTIFY_SOURCE=1',
181 my @def_ldflags = ();
182 my @def_ldflags_relro = (
185 my @def_ldflags_bindnow = (
188 my @def_ldflags_pie = (
192 my @def_ldflags_pic = (
197 # References to all flags checked by the flag checker.
201 \@def_cflags_fortify,
206 \@def_cppflags_fortify,
209 \@def_ldflags_bindnow,
212 # References to all used flags.
213 my @flag_refs_all = (
215 \@def_cppflags_fortify_bad,
218 # Renaming rules for the output so the regex parts are not visible. Also
219 # stores string values of flag regexps above, see compile_flag_regexp().
221 '-O(?:2|3)' => '-O2',
222 '-Wl,(?:-z,)?relro' => '-Wl,-z,relro',
223 '-Wl,(?:-z,)?now' => '-Wl,-z,now',
227 no_compiler_commands => 1 << 0,
228 # used by POD::Usage => 1 << 1,
229 non_verbose_build => 1 << 2,
230 flags_missing => 1 << 3,
231 hardening_wrapper => 1 << 4,
232 invalid_cmake => 1 << 5,
236 no_compiler_commands => 'I-no-compiler-commands',
237 non_verbose_build => 'W-compiler-flags-hidden',
238 flags_missing => 'W-dpkg-buildflags-missing',
239 hardening_wrapper => 'I-hardening-wrapper-used',
240 invalid_cmake => 'I-invalid-cmake-used',
243 # Statistics of missing flags and non-verbose build commands. Used for
247 preprocess_missing => 0,
249 compile_missing => 0,
251 compile_cpp_missing => 0,
255 commands_nonverbose => 0,
258 # Use colored (ANSI) output?
265 my ($message, $missing_flags_ref, $flag_renames_ref, $line) = @_;
267 # Get string value of qr//-escaped regexps and if requested rename them.
268 my @missing_flags = map {
269 $flag_renames_ref->{$_}
270 } @{$missing_flags_ref};
272 my $flags = join ' ', @missing_flags;
273 printf '%s (%s)%s %s',
274 error_color($message, 'red'), $flags, error_color(':', 'yellow'),
277 sub error_non_verbose_build {
281 error_color('NONVERBOSE BUILD', 'red'),
282 error_color(':', 'yellow'),
285 sub error_invalid_cmake {
289 error_color('INVALID CMAKE', 'red'),
290 error_color(':', 'yellow'),
293 sub error_hardening_wrapper {
295 error_color('HARDENING WRAPPER', 'red'),
296 error_color(':', 'yellow'),
297 'no checks possible, aborting';
300 my ($message, $color) = @_;
303 return Term::ANSIColor::colored($message, $color);
310 my ($line, @flags) = @_;
312 foreach my $flag (@flags) {
313 return 1 if $line =~ /$flag/;
319 my ($line, $missing_flags_ref, @flags) = @_;
321 my @missing_flags = ();
322 foreach my $flag (@flags) {
323 if (not $line =~ /$flag/) {
324 push @missing_flags, $flag;
328 return 1 if scalar @missing_flags == 0;
330 @{$missing_flags_ref} = @missing_flags;
334 sub cppflags_fortify_broken {
335 my ($line, $missing_flags) = @_;
337 # This doesn't take the position into account, but is a simple solution.
338 # And if the build system tries to force -D_FORTIFY_SOURCE=0/1, something
341 if (any_flags_used($line, @def_cppflags_fortify_bad)) {
342 # $def_cppflags_fortify[0] must be -D_FORTIFY_SOURCE=2!
343 push @{$missing_flags}, $def_cppflags_fortify[0];
350 # Modifies $missing_flags_ref array.
351 sub pic_pie_conflict {
352 my ($line, $pie, $missing_flags_ref, @flags_pie) = @_;
354 return 0 if not $pie;
355 return 0 if not any_flags_used($line, @def_ldflags_pic);
357 my %flags = map { $_ => 1 } @flags_pie;
359 # Remove all PIE flags from @missing_flags as they are not required with
362 not exists $flags{$_}
363 } @{$missing_flags_ref};
364 @{$missing_flags_ref} = @result;
366 # We got a conflict when no flags are left, thus only PIE flags were
367 # missing. If other flags were missing abort because the conflict is not
369 return scalar @result == 0;
372 sub is_non_verbose_build {
373 my ($line, $next_line, $skip_ref) = @_;
375 if (not (index($line, 'checking if you want to see long compiling messages... no') == 0
376 or $line =~ /^\s*\[?(?:CC|CCLD|C\+\+|CXX|CXXLD|LD|LINK)\]?\s+(.+?)$/
377 or $line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/
378 or $line =~ /^\s*[Bb]uilding (?:program|shared library)\s+(.+?)$/
379 or $line =~ /^\s*\[[\d ]+%\] Building (?:C|CXX) object (.+?)$/)) {
385 # C++ compiler setting.
386 return 0 if $line =~ /^\s*C\+\+.+?:\s+(?:yes|no)\s*$/;
387 # "Compiling" with no file name.
388 if ($line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/) {
389 # $file_extension_regex may need spaces around the filename.
390 return 0 if not " $1 " =~ /$file_extension_regex/o;
395 # On the first pass we only check if this line is verbose or not.
396 return 1 if not defined $next_line;
398 # Second pass, we have access to the next line.
401 # CMake and other build systems print the non-verbose messages also when
402 # building verbose. If a compiler and the file name occurs in the next
403 # line, treat it as verbose build.
405 # Get filename, we can't use the complete path as only parts of it are
406 # used in the real compiler command.
407 $file =~ m{/([^/\s]+)$};
410 if (index($next_line, $file) != -1 and $next_line =~ /$cc_regex/o) {
411 # We still have to skip the current line as it doesn't contain any
422 my ($flag_refs_ref, $flag_renames_ref, @flags) = @_;
424 my %removes = map { $_ => 1 } @flags;
425 foreach my $flags (@{$flag_refs_ref}) {
427 # Flag found as string.
428 not exists $removes{$_}
429 # Flag found as string representation of regexp.
430 and (not defined $flag_renames_ref->{$_}
431 or not exists $removes{$flag_renames_ref->{$_}})
436 sub compile_flag_regexp {
437 my ($flag_renames_ref, @flags) = @_;
440 foreach my $flag (@flags) {
441 # Store flag name in replacement string for correct flags in messages
442 # with qr//ed flag regexps.
443 $flag_renames_ref->{qr/\s$flag(?:\s|\\)/}
444 = (exists $flag_renames_ref->{$flag})
445 ? $flag_renames_ref->{$flag}
448 # Compile flag regexp for faster execution.
449 push @result, qr/\s$flag(?:\s|\\)/;
454 sub extension_found {
455 my ($extensions_ref, @extensions) = @_;
458 foreach my $extension (@extensions) {
459 if (exists $extensions_ref->{$extension}) {
470 # Parse command line arguments.
472 my $option_version = 0;
474 my $option_bindnow = 0;
475 my @option_ignore_arch = ();
476 my @option_ignore_flag = ();
477 my @option_ignore_arch_flag = ();
478 my @option_ignore_line = ();
479 my @option_ignore_arch_line = ();
481 my $option_arch = undef;
482 my $option_buildd = 0;
484 if (not Getopt::Long::GetOptions(
485 'help|h|?' => \$option_help,
486 'version' => \$option_version,
488 'pie' => \$option_pie,
489 'bindnow' => \$option_bindnow,
490 'all' => \$option_all,
492 'ignore-arch=s' => \@option_ignore_arch,
493 'ignore-flag=s' => \@option_ignore_flag,
494 'ignore-arch-flag=s' => \@option_ignore_arch_flag,
495 'ignore-line=s' => \@option_ignore_line,
496 'ignore-arch-line=s' => \@option_ignore_arch_line,
498 'color' => \$option_color,
499 'arch=s' => \$option_arch,
500 'buildd' => \$option_buildd,
503 Pod::Usage::pod2usage(2);
507 Pod::Usage::pod2usage(1);
509 if ($option_version) {
510 print "blhc $VERSION Copyright (C) 2012 Simon Ruderich
512 This program is free software: you can redistribute it and/or modify
513 it under the terms of the GNU General Public License as published by
514 the Free Software Foundation, either version 3 of the License, or
515 (at your option) any later version.
517 This program is distributed in the hope that it will be useful,
518 but WITHOUT ANY WARRANTY; without even the implied warranty of
519 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
520 GNU General Public License for more details.
522 You should have received a copy of the GNU General Public License
523 along with this program. If not, see <http://www.gnu.org/licenses/>.
529 if (scalar @ARGV == 0) {
531 Pod::Usage::pod2usage(2);
534 # Don't load Term::ANSIColor in buildd mode because Term::ANSIColor is not
535 # installed on Debian's buildds.
536 if (not $option_buildd) {
537 require Term::ANSIColor;
545 # Precompiled ignores for faster lookup.
546 my %option_ignore_arch_flag = ();
547 my %option_ignore_arch_line = ();
549 # Strip flags which should be ignored.
550 if (scalar @option_ignore_flag > 0) {
551 remove_flags(\@flag_refs, \%flag_renames, @option_ignore_flag);
553 # Same for arch specific ignore flags, but only prepare here.
554 if (scalar @option_ignore_arch_flag > 0) {
555 foreach my $ignore (@option_ignore_arch_flag) {
556 my ($ignore_arch, $ignore_flag) = split ':', $ignore, 2;
558 if (not $ignore_arch or not $ignore_flag) {
559 printf STDERR 'Value "%s" invalid for option ignore-arch-flag '
560 . '("arch:flag" expected)' . "\n", $ignore;
562 Pod::Usage::pod2usage(2);
565 push @{$option_ignore_arch_flag{$ignore_arch}}, $ignore_flag;
569 # Precompile all flag regexps. any_flags_used(), all_flags_used() get a lot
571 foreach my $flags (@flag_refs_all) {
572 @{$flags} = compile_flag_regexp(\%flag_renames, @{$flags});
575 # Precompile ignore line regexps, also anchor at beginning and end of line.
576 foreach my $ignore (@option_ignore_line) {
577 $ignore = qr/^$ignore$/;
579 # Same for arch specific ignore lines.
580 if (scalar @option_ignore_arch_line > 0) {
581 foreach my $ignore (@option_ignore_arch_line) {
582 my ($ignore_arch, $ignore_line) = split ':', $ignore, 2;
584 if (not $ignore_arch or not $ignore_line) {
585 printf STDERR 'Value "%s" invalid for option ignore-arch-line '
586 . '("arch:line" expected)' . "\n", $ignore;
588 Pod::Usage::pod2usage(2);
591 push @{$option_ignore_arch_line{$ignore_arch}}, qr/^$ignore_line$/;
599 foreach my $file (@ARGV) {
600 print "checking '$file'...\n" if scalar @ARGV > 1;
602 open my $fh, '<', $file or die $!;
604 # Architecture of this file.
605 my $arch = $option_arch;
607 # Hardening options. Not all architectures support all hardening options.
608 my $harden_format = 1;
609 my $harden_fortify = 1;
610 my $harden_stack = 1;
611 my $harden_relro = 1;
612 my $harden_bindnow = $option_bindnow; # defaults to 0
613 my $harden_pie = $option_pie; # defaults to 0
615 while (my $line = <$fh>) {
616 # Detect architecture automatically unless overridden. For buildd logs
617 # only, doesn't use the dpkg-buildpackage header. Necessary to ignore
618 # build logs which aren't built (wrong architecture, build error,
621 and $line =~ /^Architecture: (.+)$/) {
625 # dpkg-buildflags only provides hardening flags since 1.16.1, don't
626 # check for hardening flags in buildd mode if an older dpkg-dev is
627 # used. Default flags (-g -O2) are still checked.
629 # Packages which were built before 1.16.1 but used their own hardening
630 # flags are not checked.
632 and index($line, 'Toolchain package versions: ') == 0) {
633 require Dpkg::Version;
634 if (not $line =~ /\bdpkg-dev_(\S+)/
635 or Dpkg::Version::version_compare($1, '1.16.1') < 0) {
645 # The following two versions of CMake in Debian obeyed CPPFLAGS, but
646 # this was later dropped because upstream rejected the patch. Thus
647 # build logs with these versions will have fortify hardening flags
648 # enabled, even though they may be not correctly set and are missing
649 # when build with later CMake versions. Thanks to Aron Xu for letting
651 if (index($line, 'Package versions: ') == 0
652 and $line =~ /\bcmake_(\S+)/
653 and ($1 eq '2.8.7-1' or $1 eq '2.8.7-2')) {
654 if (not $option_buildd) {
655 error_invalid_cmake($1);
657 print "$buildd_tag{invalid_cmake} $1\n";
659 $exit |= $exit_code{invalid_cmake};
662 # If hardening wrapper is used (wraps calls to gcc and adds hardening
663 # flags automatically) we can't perform any checks, abort.
664 if (index($line, 'Build-Depends: ') == 0
665 and $line =~ /\bhardening-wrapper\b/) {
666 if (not $option_buildd) {
667 error_hardening_wrapper();
669 print "$buildd_tag{hardening_wrapper}\n";
671 $exit |= $exit_code{hardening_wrapper};
675 # We skip over unimportant lines at the beginning of the log to
676 # prevent false positives.
677 last if index($line, 'dpkg-buildpackage: ') == 0;
680 # Input lines, contain only the lines with compiler commands.
683 my $continuation = 0;
684 my $complete_line = undef;
685 while (my $line = <$fh>) {
686 # And stop at the end of the build log. Package details (reported by
687 # the buildd logs) are not important for us. This also prevents false
689 last if $line =~ /^Build finished at \d{8}-\d{4}$/;
691 # Detect architecture automatically unless overridden.
693 and $line =~ /^dpkg-buildpackage: host architecture (.+)$/) {
697 # Ignore compiler warnings for now.
698 next if $line =~ /$warning_regex/o;
700 if (not $option_buildd and index($line, "\033") != -1) { # esc
701 # Remove all ANSI color sequences which are sometimes used in
702 # non-verbose builds.
703 $line = Term::ANSIColor::colorstrip($line);
704 # Also strip '\0xf' (delete previous character), used by Elinks'
707 # And "ESC(B" which seems to be used on armhf and hurd (not sure
709 $line =~ s/\033\(B//g;
712 # Check if this line indicates a non verbose build.
713 my $non_verbose = is_non_verbose_build($line);
715 # One line may contain multiple commands (";"). Treat each one as
716 # single line. parse_line() is slow, only use it when necessary.
717 my @line = (index($line, ';') == -1)
720 # Ensure newline at the line end - necessary for
721 # correct parsing later.
724 } Text::ParseWords::parse_line(';', 1, $line);
725 foreach my $line (@line) {
729 # Join lines, but leave the "\" in place so it's clear where
730 # the original line break was.
731 chomp $complete_line;
732 $complete_line .= ' ' . $line;
734 # Line continuation, line ends with "\".
735 if ($line =~ /\\$/) {
737 # Start line continuation.
738 if (not defined $complete_line) {
739 $complete_line = $line;
744 # Use the complete line if a line continuation occurred.
745 if (defined $complete_line) {
746 $line = $complete_line;
747 $complete_line = undef;
750 # Ignore lines with no compiler commands.
751 next if not $non_verbose
752 and not $line =~ /\b$cc_regex(?:\s|\\)/o;
753 # Ignore lines with no filenames with extensions. May miss some
754 # non-verbose builds (e.g. "gcc -o test" [sic!]), but shouldn't be
755 # a problem as the log will most likely contain other non-verbose
756 # commands which are detected.
757 next if not $non_verbose
758 and not $line =~ /$file_extension_regex/o;
760 # Ignore false positives.
762 # `./configure` output.
763 next if not $non_verbose
764 and $line =~ /^(?:checking|[Cc]onfigure:) /;
765 next if $line =~ /^\s*(?:Host\s+)?(?:C(?:\+\+)?\s+)?
766 [Cc]ompiler[\s.]*:?\s+
768 next if $line =~ /^\s*(?:- )?(?:HOST_)?(?:CC|CXX)\s*=\s*$cc_regex_full\s*$/o;
769 # `moc-qt4`, contains '-I/usr/share/qt4/mkspecs/linux-g++' (or
770 # similar for other architectures) which gets recognized as a
771 # compiler line. Ignore it.
772 next if $line =~ m{^/usr/bin/moc-qt4
774 -I/usr/share/qt4/mkspecs/[a-z]+-g\++(?:-64)?
777 # Check if additional hardening options were used. Used to ensure
778 # they are used for the complete build.
779 $harden_pie = 1 if any_flags_used($line, @def_cflags_pie,
781 $harden_bindnow = 1 if any_flags_used($line, @def_ldflags_bindnow);
789 # Ignore arch if requested.
790 if (scalar @option_ignore_arch > 0 and $arch) {
791 foreach my $ignore (@option_ignore_arch) {
792 if ($arch eq $ignore) {
793 print "ignoring architecture '$arch'\n";
799 if (scalar @input == 0) {
800 if (not $option_buildd) {
801 print "No compiler commands!\n";
803 print "$buildd_tag{no_compiler_commands}\n";
805 $exit |= $exit_code{no_compiler_commands};
809 if ($option_buildd) {
810 $statistics{commands} += scalar @input;
813 # Option or auto detected.
815 # The following was partially copied from dpkg-dev 1.16.1.2
816 # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, add_hardening_flags()),
817 # copyright Raphaël Hertzog <hertzog@debian.org>, Kees Cook
818 # <kees@debian.org>, Canonical, Ltd. licensed under GPL version 2 or
819 # later. Keep it in sync.
822 my ($abi, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch);
824 # Disable unsupported hardening options.
825 if ($cpu =~ /^(?:ia64|alpha|mips|mipsel|hppa)$/ or $arch eq 'arm') {
828 if ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
835 my @cflags = @def_cflags;
836 my @cxxflags = @def_cxxflags;
837 my @cppflags = @def_cppflags;
838 my @ldflags = @def_ldflags;
839 # Check the specified hardening options, same order as dpkg-buildflags.
841 @cflags = (@cflags, @def_cflags_pie);
842 @cxxflags = (@cxxflags, @def_cflags_pie);
843 @ldflags = (@ldflags, @def_ldflags_pie);
846 @cflags = (@cflags, @def_cflags_stack);
847 @cxxflags = (@cxxflags, @def_cflags_stack);
849 if ($harden_fortify) {
850 @cflags = (@cflags, @def_cflags_fortify);
851 @cxxflags = (@cxxflags, @def_cflags_fortify);
852 @cppflags = (@cppflags, @def_cppflags_fortify);
854 if ($harden_format) {
855 @cflags = (@cflags, @def_cflags_format);
856 @cxxflags = (@cxxflags, @def_cflags_format);
859 @ldflags = (@ldflags, @def_ldflags_relro);
861 if ($harden_bindnow) {
862 @ldflags = (@ldflags, @def_ldflags_bindnow);
865 # Ignore flags for this arch if requested.
866 if ($arch and exists $option_ignore_arch_flag{$arch}) {
867 my @flag_refs = (\@cflags, \@cxxflags, \@cppflags, \@ldflags);
869 remove_flags(\@flag_refs,
871 @{$option_ignore_arch_flag{$arch}});
874 my @ignore_line = @option_ignore_line;
875 # Ignore lines for this arch if requested.
876 if ($arch and exists $option_ignore_arch_line{$arch}) {
877 @ignore_line = (@ignore_line, @{$option_ignore_arch_line{$arch}});
881 for (my $i = 0; $i < scalar @input; $i++) {
882 my $line = $input[$i];
884 # Ignore line if requested.
885 foreach my $ignore (@ignore_line) {
886 next LINE if $line =~ /$ignore/;
890 if (is_non_verbose_build($line, $input[$i + 1], \$skip)) {
891 if (not $option_buildd) {
892 error_non_verbose_build($line);
894 $statistics{commands_nonverbose}++;
896 $exit |= $exit_code{non_verbose_build};
899 # Even if it's a verbose build, we might have to skip this line.
902 # Remove everything until and including the compiler command. Makes
903 # checks easier and faster.
904 $line =~ s/^.*?$cc_regex//o;
905 # "([...] test.c)" is not detected as 'test.c' - fix this by removing
906 # the brace and similar characters.
907 $line =~ s/['")]+$//;
909 # Skip unnecessary tests when only preprocessing.
910 my $flag_preprocess = 0;
917 # Preprocess, compile, assemble.
918 if ($line =~ /\s(-E|-S|-c)\b/) {
920 $flag_preprocess = 1 if $1 eq '-E';
921 $compile = 1 if $1 eq '-S' or $1 eq '-c';
922 # Dependency generation for Makefiles. The other flags (-MF -MG -MP
923 # -MT -MQ) are always used with -M/-MM.
924 } elsif ($line =~ /\s(?:-M|-MM)\b/) {
926 # Otherwise assume we are linking.
931 # -MD/-MMD also cause dependency generation, but they don't imply -E!
932 if ($line =~ /\s(?:-MD|-MMD)\b/) {
934 $flag_preprocess = 0;
937 # Dependency generation for Makefiles, no preprocessing or other flags
941 # Get all file extensions on this line.
942 my @extensions = $line =~ /$file_extension_regex/go;
943 # Ignore all unknown extensions to speedup the search below.
944 @extensions = grep { exists $extension{$_} } @extensions;
946 # These file types don't require preprocessing.
947 if (extension_found(\%extensions_no_preprocess, @extensions)) {
950 # These file types require preprocessing.
951 if (extension_found(\%extensions_preprocess, @extensions)) {
955 # If there are source files then it's compiling/linking in one step
956 # and we must check both. We only check for source files here, because
957 # header files cause too many false positives.
958 if (not $flag_preprocess
959 and extension_found(\%extensions_compile_link, @extensions)) {
960 # Assembly files don't need CFLAGS.
961 if (not extension_found(\%extensions_compile, @extensions)
962 and extension_found(\%extensions_no_compile, @extensions)) {
970 # Assume CXXFLAGS are required when a C++ file is specified in the
974 and extension_found(\%extensions_compile_cpp, @extensions)) {
979 if ($option_buildd) {
980 $statistics{preprocess}++ if $preprocess;
981 $statistics{compile}++ if $compile;
982 $statistics{compile_cpp}++ if $compile_cpp;
983 $statistics{link}++ if $link;
986 # Check hardening flags.
988 if ($compile and not all_flags_used($line, \@missing, @cflags)
989 # Libraries linked with -fPIC don't have to (and can't) be
990 # linked with -fPIE as well. It's no error if only PIE flags
992 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
993 # Assume dpkg-buildflags returns the correct flags.
994 and index($line, '`dpkg-buildflags --get CFLAGS`') == -1) {
995 if (not $option_buildd) {
996 error_flags('CFLAGS missing', \@missing, \%flag_renames, $input[$i]);
998 $statistics{compile_missing}++;
1000 $exit |= $exit_code{flags_missing};
1001 } elsif ($compile_cpp and not all_flags_used($line, \@missing, @cflags)
1002 # Libraries linked with -fPIC don't have to (and can't) be
1003 # linked with -fPIE as well. It's no error if only PIE flags
1005 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1006 # Assume dpkg-buildflags returns the correct flags.
1007 and index($line, '`dpkg-buildflags --get CXXFLAGS`') == -1) {
1008 if (not $option_buildd) {
1009 error_flags('CXXFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1011 $statistics{compile_cpp_missing}++;
1013 $exit |= $exit_code{flags_missing};
1016 and (not all_flags_used($line, \@missing, @cppflags)
1017 # The fortify flag might be overwritten, detect that.
1019 and cppflags_fortify_broken($line, \@missing)))
1020 # Assume dpkg-buildflags returns the correct flags.
1021 and index($line, '`dpkg-buildflags --get CPPFLAGS`') == -1) {
1022 if (not $option_buildd) {
1023 error_flags('CPPFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1025 $statistics{preprocess_missing}++;
1027 $exit |= $exit_code{flags_missing};
1029 if ($link and not all_flags_used($line, \@missing, @ldflags)
1030 # Same here, -fPIC conflicts with -fPIE.
1031 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_ldflags_pie)
1032 # Assume dpkg-buildflags returns the correct flags.
1033 and index($line, '`dpkg-buildflags --get LDFLAGS`') == -1) {
1034 if (not $option_buildd) {
1035 error_flags('LDFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1037 $statistics{link_missing}++;
1039 $exit |= $exit_code{flags_missing};
1044 # Print statistics for buildd mode, only output in this mode.
1045 if ($option_buildd) {
1048 if ($statistics{preprocess_missing}) {
1049 push @warning, sprintf 'CPPFLAGS %d (of %d)',
1050 $statistics{preprocess_missing},
1051 $statistics{preprocess};
1053 if ($statistics{compile_missing}) {
1054 push @warning, sprintf 'CFLAGS %d (of %d)',
1055 $statistics{compile_missing},
1056 $statistics{compile};
1058 if ($statistics{compile_cpp_missing}) {
1059 push @warning, sprintf 'CXXFLAGS %d (of %d)',
1060 $statistics{compile_cpp_missing},
1061 $statistics{compile_cpp};
1063 if ($statistics{link_missing}) {
1064 push @warning, sprintf 'LDFLAGS %d (of %d)',
1065 $statistics{link_missing},
1068 if (scalar @warning) {
1069 local $" = ', '; # array join string
1070 print "$buildd_tag{flags_missing} @warning missing\n";
1073 if ($statistics{commands_nonverbose}) {
1074 printf "$buildd_tag{non_verbose_build} %d (of %d) hidden\n",
1075 $statistics{commands_nonverbose},
1076 $statistics{commands},
1088 blhc - build log hardening check, checks build logs for missing hardening flags
1092 B<blhc> [I<options>] I<< <dpkg-buildpackage build log file>.. >>
1096 blhc is a small tool which checks build logs for missing hardening flags. It's
1097 licensed under the GPL 3 or later.
1099 It's designed to check build logs generated by Debian's dpkg-buildpackage (or
1100 tools using dpkg-buildpackage like pbuilder or the official buildd build logs)
1101 to help maintainers detect missing hardening flags in their packages.
1103 If there's no output, no flags are missing and the build log is fine.
1111 Force check for all +all (+pie, +bindnow) hardening flags. By default it's
1114 =item B<--arch> I<architecture>
1116 Set the specific architecture (e.g. amd64, armel, etc.), automatically
1117 disables hardening flags not available on this architecture. Is detected
1118 automatically if dpkg-buildpackage is used.
1122 Force check for all +bindnow hardening flags. By default it's auto detected.
1126 Special mode for buildds when automatically parsing log files. The following
1127 changes are in effect:
1133 Print tags instead of normal warnings, see L</"BUILDD TAGS"> for a list of
1138 Don't check hardening flags in old log files (if dpkg-dev << 1.16.1 is
1143 Don't require Term::ANSIColor.
1149 Use colored (ANSI) output for warning messages.
1151 =item B<--ignore-arch> I<arch>
1153 Ignore build logs from architectures matching I<arch>. I<arch> is a string.
1155 Used to prevent false positives. This option can be specified multiple times.
1157 =item B<--ignore-arch-flag> I<arch>:I<flag>
1159 Like B<--ignore-flag>, but only ignore flag on I<arch>.
1161 =item B<--ignore-arch-line> I<arch>:I<line>
1163 Like B<--ignore-line>, but only ignore line on I<arch>.
1165 =item B<--ignore-flag> I<flag>
1167 Don't print an error when the specific flag is missing in a compiler line.
1168 I<flag> is a string.
1170 Used to prevent false positives. This option can be specified multiple times.
1172 =item B<--ignore-line> I<regex>
1174 Ignore lines matching the given Perl regex. I<regex> is automatically anchored
1175 at the beginning and end of the line to prevent false negatives.
1177 B<NOTE>: Not the input lines are checked, but the lines which are displayed in
1178 warnings (which have line continuation resolved).
1180 Used to prevent false positives. This option can be specified multiple times.
1184 Force check for all +pie hardening flags. By default it's auto detected.
1186 =item B<-h -? --help>
1188 Print available options.
1192 Print version number and license.
1196 Auto detection for B<--pie> and B<--bindnow> only works if at least one
1197 command uses the required hardening flag (e.g. -fPIE). Then it's required for
1198 all other commands as well.
1202 Normal usage, parse a single log file.
1204 blhc path/to/log/file
1206 If there's no output, no flags are missing and the build log is fine.
1208 Parse multiple log files. The exit code is ORed over all files.
1210 blhc path/to/directory/with/log/files/*
1212 Don't treat missing C<-g> as error:
1214 blhc --ignore-flag -g path/to/log/file
1216 Don't treat missing C<-pie> on kfreebsd-amd64 as error:
1218 blhc --ignore-arch-flag kfreebsd-amd64:-pie path/to/log/file
1220 Ignore lines consisting exactly of C<./script gcc file> which would cause a
1223 blhc --ignore-line '\./script gcc file' path/to/log/file
1225 Ignore lines matching C<./script gcc file> somewhere in the line.
1227 blhc --ignore-line '.*\./script gcc file.*' path/to/log/file
1229 Use blhc with pbuilder.
1231 pbuilder path/to/package.dsc | tee path/log/file
1232 blhc path/to/file || echo flags missing
1236 The following tags are used in I<--buildd> mode. In braces the additional data
1241 =item B<I-hardening-wrapper-used>
1243 The package uses hardening-wrapper which intercepts calls to gcc and adds
1244 hardening flags. The build log doesn't contain any hardening flags and thus
1245 can't be checked by blhc.
1247 =item B<W-compiler-flags-hidden> (summary of hidden lines)
1249 Build log contains lines which hide the real compiler flags. For example:
1256 Most of the time either C<export V=1> or C<export verbose=1> in
1257 F<debian/rules> fixes builds with hidden compiler flags. Sometimes C<.SILENT>
1258 in a F<Makefile> must be removed. And as last resort the F<Makefile> must be
1259 patched to remove the C<@>s hiding the real compiler commands.
1261 =item B<W-dpkg-buildflags-missing> (summary of missing flags)
1263 CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS missing.
1265 =item B<I-invalid-cmake-used> (version)
1267 By default CMake ignores CPPFLAGS thus missing those hardening flags. Debian
1268 patched CMake in versions 2.8.7-1 and 2.8.7-2 to respect CPPFLAGS, but this
1269 patch was rejected by upstream and later reverted in Debian. Thus those two
1270 versions show correct usage of CPPFLAGS even if the package doesn't correctly
1271 handle them (for example by passing them to CFLAGS). To prevent false
1272 negatives just blacklist those two versions.
1274 =item B<I-no-compiler-commands>
1276 No compiler commands were detected. Either the log contains none or they were
1277 not correctly detected by blhc (please report the bug in this case).
1283 The exit status is a "bit mask", each listed status is ORed when the error
1284 condition occurs to get the result.
1294 No compiler commands were found.
1298 Invalid arguments/options given to blhc.
1306 Missing hardening flags.
1310 Hardening wrapper detected, no tests performed.
1314 Invalid CMake version used. See B<I-invalid-cmake-used> under L</"BUILDD
1315 TAGS"> for a detailed explanation.
1321 Simon Ruderich, E<lt>simon@ruderich.orgE<gt>
1323 Thanks to to Bernhard R. Link E<lt>brlink@debian.orgE<gt> and Jaria Alto
1324 E<lt>jari.aalto@cante.netE<gt> for their valuable input and suggestions.
1326 =head1 COPYRIGHT AND LICENSE
1328 Copyright (C) 2012 by Simon Ruderich
1330 This program is free software: you can redistribute it and/or modify
1331 it under the terms of the GNU General Public License as published by
1332 the Free Software Foundation, either version 3 of the License, or
1333 (at your option) any later version.
1335 This program is distributed in the hope that it will be useful,
1336 but WITHOUT ANY WARRANTY; without even the implied warranty of
1337 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1338 GNU General Public License for more details.
1340 You should have received a copy of the GNU General Public License
1341 along with this program. If not, see <http://www.gnu.org/licenses/>.
1345 L<hardening-check(1)>, L<dpkg-buildflags(1)>