]> ruderich.org/simon Gitweb - blhc/blhc.git/blob - bin/blhc
Ignore missing -Wformat -Werror=format-security for Ada files.
[blhc/blhc.git] / bin / blhc
1 #!/usr/bin/perl
2
3 # Build log hardening check, checks build logs for missing hardening flags.
4
5 # Copyright (C) 2012  Simon Ruderich
6 #
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.
11 #
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.
16 #
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/>.
19
20
21 use strict;
22 use warnings;
23
24 use Getopt::Long ();
25 use Text::ParseWords ();
26
27 our $VERSION = '0.03';
28
29
30 # CONSTANTS/VARIABLES
31
32 # Regex to catch compiler commands.
33 my $cc_regex = qr/
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"
38     /x;
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)?-)?
43     $cc_regex
44     /x;
45 # Regex to check if a line contains a compiler command.
46 my $cc_regex_normal = qr/
47     \b$cc_regex(?:\s|\\)
48     /x;
49 # Regex to catch (GCC) compiler warnings.
50 my $warning_regex = qr/^(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]$/;
51
52 # List of source file extensions which require preprocessing.
53 my @source_preprocess_compile_cpp = (
54     # C++
55     qw( cc cp cxx cpp CPP c++ C ),
56     # Objective-C++
57     qw( mm M ),
58 );
59 my @source_preprocess_compile = (
60     # C
61     qw( c ),
62     # Objective-C
63     qw( m ),
64     # (Objective-)C++
65     @source_preprocess_compile_cpp,
66     # Fortran
67     qw( F FOR fpp FPP FTN F90 F95 F03 F08 ),
68 );
69 my @source_preprocess_no_compile = (
70     # Assembly
71     qw( S sx ),
72 );
73 my @source_preprocess = (
74     @source_preprocess_compile,
75     @source_preprocess_no_compile,
76 );
77 # List of source file extensions which don't require preprocessing.
78 my @source_no_preprocess_compile_cpp = (
79     # C++
80     qw( ii ),
81     # Objective-C++
82     qw( mii ),
83 );
84 my @source_no_preprocess_compile_ada = (
85     # Ada body
86     qw( adb ),
87     # If you add another file, fix use of @source_no_preprocess_compile_ada
88     # below (search for $compile_ada).
89 );
90 my @source_no_preprocess_compile = (
91     # C
92     qw( i ),
93     # (Objective-)C++
94     @source_no_preprocess_compile_cpp,
95     # Objective-C
96     qw( mi ),
97     # Fortran
98     qw( f for ftn f90 f95 f03 f08 ),
99     # Ada
100     @source_no_preprocess_compile_ada,
101 );
102 my @source_no_preprocess_no_compile = (
103     # Assembly
104     qw( s ),
105     # Ada specification
106     qw( ads ),
107 );
108 my @source_no_preprocess = (
109     @source_no_preprocess_compile,
110     @source_no_preprocess_no_compile,
111 );
112 # List of header file extensions which require preprocessing.
113 my @header_preprocess = (
114     # C, C++, Objective-C, Objective-C++
115     qw( h ),
116     # C++
117     qw( hh H hp hxx hpp HPP h++ tcc ),
118 );
119 # Object files.
120 my @object = (
121     # Normal object files.
122     qw ( o ),
123     # Libtool object files.
124     qw ( lo la ),
125     # Dynamic libraries. bzip2 uses .sho.
126     qw ( so sho ),
127     # Static libraries.
128     qw ( a ),
129 );
130
131 # Hashes for fast extensions lookup to check if a file falls in one of these
132 # categories.
133 my %extensions_no_preprocess = map { $_ => 1 } (
134     # There's no @header_no_preprocess.
135     @source_no_preprocess,
136 );
137 my %extensions_preprocess = map { $_ => 1 } (
138     @header_preprocess,
139     @source_preprocess,
140 );
141 my %extensions_compile_link = map { $_ => 1 } (
142     @source_preprocess,
143     @source_no_preprocess,
144 );
145 my %extensions_compile = map { $_ => 1 } (
146     @source_preprocess_compile,
147     @source_no_preprocess_compile,
148 );
149 my %extensions_no_compile = map { $_ => 1 } (
150     @source_preprocess_no_compile,
151     @source_no_preprocess_no_compile,
152 );
153 my %extensions_compile_cpp = map { $_ => 1 } (
154     @source_preprocess_compile_cpp,
155     @source_no_preprocess_compile_cpp,
156 );
157 my %extensions_object = map { $_ => 1 } (
158     @object,
159 );
160 my %extension = map { $_ => 1 } (
161     @source_no_preprocess,
162     @header_preprocess,
163     @source_preprocess,
164     @object,
165 );
166
167 # Regexp to match file extensions.
168 my $file_extension_regex = qr/
169     \s
170     \S+             # Filename without extension.
171     \.
172     ([^\/\\.,;:\s]+)# File extension.
173     (?=\s|\\)       # At end of word. Can't use \b because some files have non
174                     # word characters at the end and because \b matches double
175                     # extensions (like .cpp.o). Works always as all lines are
176                     # terminated with "\n".
177     /x;
178
179 # Expected (hardening) flags. All flags are used as regexps.
180 my @def_cflags = (
181     '-g',
182     '-O(?:2|3)',
183 );
184 my @def_cflags_format = (
185     '-Wformat',
186     '-Werror=format-security', # implies -Wformat-security
187 );
188 my @def_cflags_fortify = (
189     # fortify needs at least -O1, but -O2 is recommended anyway
190 );
191 my @def_cflags_stack = (
192     '-fstack-protector',
193     '--param=ssp-buffer-size=4',
194 );
195 my @def_cflags_pie = (
196     '-fPIE',
197 );
198 my @def_cxxflags = (
199     @def_cflags,
200 );
201 # @def_cxxflags_* is the same as @def_cflags_*.
202 my @def_cppflags = ();
203 my @def_cppflags_fortify = (
204     '-D_FORTIFY_SOURCE=2', # must be first, see cppflags_fortify_broken()
205     # If you add another flag fix hack below (search for "Hack to fix").
206 );
207 my @def_cppflags_fortify_bad = (
208     # These flags may overwrite -D_FORTIFY_SOURCE=2.
209     '-U_FORTIFY_SOURCE',
210     '-D_FORTIFY_SOURCE=0',
211     '-D_FORTIFY_SOURCE=1',
212 );
213 my @def_ldflags = ();
214 my @def_ldflags_relro = (
215     '-Wl,(?:-z,)?relro',
216 );
217 my @def_ldflags_bindnow = (
218     '-Wl,(?:-z,)?now',
219 );
220 my @def_ldflags_pie = (
221     '-fPIE',
222     '-pie',
223 );
224 my @def_ldflags_pic = (
225     '-fPIC',
226     '-fpic',
227     '-shared',
228 );
229 # References to all flags checked by the flag checker.
230 my @flag_refs = (
231     \@def_cflags,
232     \@def_cflags_format,
233     \@def_cflags_fortify,
234     \@def_cflags_stack,
235     \@def_cflags_pie,
236     \@def_cxxflags,
237     \@def_cppflags,
238     \@def_cppflags_fortify,
239     \@def_ldflags,
240     \@def_ldflags_relro,
241     \@def_ldflags_bindnow,
242     \@def_ldflags_pie,
243 );
244 # References to all used flags.
245 my @flag_refs_all = (
246     @flag_refs,
247     \@def_cppflags_fortify_bad,
248     \@def_ldflags_pic,
249 );
250 # Renaming rules for the output so the regex parts are not visible. Also
251 # stores string values of flag regexps above, see compile_flag_regexp().
252 my %flag_renames = (
253     '-O(?:2|3)'         => '-O2',
254     '-Wl,(?:-z,)?relro' => '-Wl,-z,relro',
255     '-Wl,(?:-z,)?now'   => '-Wl,-z,now',
256 );
257
258 my %exit_code = (
259     no_compiler_commands => 1 << 0,
260     # used by POD::Usage => 1 << 1,
261     non_verbose_build    => 1 << 2,
262     flags_missing        => 1 << 3,
263     hardening_wrapper    => 1 << 4,
264     invalid_cmake        => 1 << 5,
265 );
266
267 my %buildd_tag = (
268     no_compiler_commands => 'I-no-compiler-commands',
269     non_verbose_build    => 'W-compiler-flags-hidden',
270     flags_missing        => 'W-dpkg-buildflags-missing',
271     hardening_wrapper    => 'I-hardening-wrapper-used',
272     invalid_cmake        => 'I-invalid-cmake-used',
273 );
274
275 # Statistics of missing flags and non-verbose build commands. Used for
276 # $option_buildd.
277 my %statistics = (
278     preprocess          => 0,
279     preprocess_missing  => 0,
280     compile             => 0,
281     compile_missing     => 0,
282     compile_cpp         => 0,
283     compile_cpp_missing => 0,
284     link                => 0,
285     link_missing        => 0,
286     commands            => 0,
287     commands_nonverbose => 0,
288 );
289
290 # Use colored (ANSI) output?
291 my $option_color;
292
293
294 # FUNCTIONS
295
296 # Only works for single-level arrays with no undef values. Thanks to perlfaq4.
297 sub array_equal {
298     my ($first_ref, $second_ref) = @_;
299
300     return 0 if scalar @{$first_ref} != scalar @{$second_ref};
301
302     my $length = scalar @{$first_ref};
303     for (my $i = 0; $i < $length; $i++) {
304         return 0 if $first_ref->[$i] ne $second_ref->[$i];
305     }
306
307     return 1;
308 }
309
310 sub error_flags {
311     my ($message, $missing_flags_ref, $flag_renames_ref, $line) = @_;
312
313     # Get string value of qr//-escaped regexps and if requested rename them.
314     my @missing_flags = map {
315             $flag_renames_ref->{$_}
316         } @{$missing_flags_ref};
317
318     my $flags = join ' ', @missing_flags;
319     printf '%s (%s)%s %s',
320            error_color($message, 'red'), $flags, error_color(':', 'yellow'),
321            $line;
322 }
323 sub error_non_verbose_build {
324     my ($line) = @_;
325
326     printf '%s%s %s',
327            error_color('NONVERBOSE BUILD', 'red'),
328            error_color(':', 'yellow'),
329            $line;
330 }
331 sub error_invalid_cmake {
332     my ($version) = @_;
333
334     printf "%s%s %s\n",
335             error_color('INVALID CMAKE', 'red'),
336             error_color(':', 'yellow'),
337             $version;
338 }
339 sub error_hardening_wrapper {
340     printf "%s%s %s\n",
341             error_color('HARDENING WRAPPER', 'red'),
342             error_color(':', 'yellow'),
343             'no checks possible, aborting';
344 }
345 sub error_color {
346     my ($message, $color) = @_;
347
348     if ($option_color) {
349         return Term::ANSIColor::colored($message, $color);
350     } else {
351         return $message;
352     }
353 }
354
355 sub any_flags_used {
356     my ($line, @flags) = @_;
357
358     foreach my $flag (@flags) {
359         return 1 if $line =~ /$flag/;
360     }
361
362     return 0;
363 }
364 sub all_flags_used {
365     my ($line, $missing_flags_ref, @flags) = @_;
366
367     my @missing_flags = ();
368     foreach my $flag (@flags) {
369         if (not $line =~ /$flag/) {
370             push @missing_flags, $flag;
371         }
372     }
373
374     return 1 if scalar @missing_flags == 0;
375
376     @{$missing_flags_ref} = @missing_flags;
377     return 0;
378 }
379
380 sub cppflags_fortify_broken {
381     my ($line, $missing_flags) = @_;
382
383     # This doesn't take the position into account, but is a simple solution.
384     # And if the build system tries to force -D_FORTIFY_SOURCE=0/1, something
385     # is wrong anyway.
386
387     if (any_flags_used($line, @def_cppflags_fortify_bad)) {
388         # $def_cppflags_fortify[0] must be -D_FORTIFY_SOURCE=2!
389         push @{$missing_flags}, $def_cppflags_fortify[0];
390         return 1;
391     }
392
393     return 0;
394 }
395
396 # Modifies $missing_flags_ref array.
397 sub pic_pie_conflict {
398     my ($line, $pie, $missing_flags_ref, @flags_pie) = @_;
399
400     return 0 if not $pie;
401     return 0 if not any_flags_used($line, @def_ldflags_pic);
402
403     my %flags = map { $_ => 1 } @flags_pie;
404
405     # Remove all PIE flags from @missing_flags as they are not required with
406     # -fPIC.
407     my @result = grep {
408         not exists $flags{$_}
409     } @{$missing_flags_ref};
410     @{$missing_flags_ref} = @result;
411
412     # We got a conflict when no flags are left, thus only PIE flags were
413     # missing. If other flags were missing abort because the conflict is not
414     # the problem.
415     return scalar @result == 0;
416 }
417
418 sub is_non_verbose_build {
419     my ($line, $next_line, $skip_ref) = @_;
420
421     if (not (index($line, 'checking if you want to see long compiling messages... no') == 0
422                 or $line =~ /^\s*\[?(?:CC|CCLD|C\+\+|CXX|CXXLD|LD|LINK)\]?\s+(.+?)$/
423                 or $line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/
424                 or $line =~ /^\s*[Bb]uilding (?:program|shared library)\s+(.+?)$/
425                 or $line =~ /^\s*\[[\d ]+%\] Building (?:C|CXX) object (.+?)$/)) {
426         return 0;
427     }
428
429     # False positives.
430     #
431     # C++ compiler setting.
432     return 0 if $line =~ /^\s*C\+\+.+?:\s+(?:yes|no)\s*$/;
433     # "Compiling" with no file name.
434     if ($line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/) {
435         # $file_extension_regex may need spaces around the filename.
436         return 0 if not " $1 " =~ /$file_extension_regex/o;
437     }
438
439     my $file = $1;
440
441     # On the first pass we only check if this line is verbose or not.
442     return 1 if not defined $next_line;
443
444     # Second pass, we have access to the next line.
445     ${$skip_ref} = 0;
446
447     # CMake and other build systems print the non-verbose messages also when
448     # building verbose. If a compiler and the file name occurs in the next
449     # line, treat it as verbose build.
450     if (defined $file) {
451         # Get filename, we can't use the complete path as only parts of it are
452         # used in the real compiler command.
453         $file =~ m{/([^/\s]+)$};
454         $file = $1;
455
456         if (index($next_line, $file) != -1 and $next_line =~ /$cc_regex/o) {
457             # Not a non-verbose line, but we still have to skip the current line
458             # as it doesn't contain any compiler commands.
459             ${$skip_ref} = 1;
460             return 0;
461         }
462     }
463
464     return 1;
465 }
466
467 sub remove_flags {
468     my ($flag_refs_ref, $flag_renames_ref, @flags) = @_;
469
470     my %removes = map { $_ => 1 } @flags;
471     foreach my $flags (@{$flag_refs_ref}) {
472         @{$flags} = grep {
473             # Flag found as string.
474             not exists $removes{$_}
475             # Flag found as string representation of regexp.
476                 and (not defined $flag_renames_ref->{$_}
477                         or not exists $removes{$flag_renames_ref->{$_}})
478         } @{$flags};
479     }
480 }
481
482 sub compile_flag_regexp {
483     my ($flag_renames_ref, @flags) = @_;
484
485     my @result = ();
486     foreach my $flag (@flags) {
487         # Store flag name in replacement string for correct flags in messages
488         # with qr//ed flag regexps.
489         $flag_renames_ref->{qr/\s$flag(?:\s|\\)/}
490             = (exists $flag_renames_ref->{$flag})
491                 ? $flag_renames_ref->{$flag}
492                 : $flag;
493
494         # Compile flag regexp for faster execution.
495         push @result, qr/\s$flag(?:\s|\\)/;
496     }
497     return @result;
498 }
499
500 sub extension_found {
501     my ($extensions_ref, @extensions) = @_;
502
503     my $found = 0;
504     foreach my $extension (@extensions) {
505         if (exists $extensions_ref->{$extension}) {
506             $found = 1;
507             last;
508         }
509     }
510     return $found;
511 }
512
513
514 # MAIN
515
516 # Parse command line arguments.
517 my $option_help             = 0;
518 my $option_version          = 0;
519 my $option_pie              = 0;
520 my $option_bindnow          = 0;
521 my @option_ignore_arch      = ();
522 my @option_ignore_flag      = ();
523 my @option_ignore_arch_flag = ();
524 my @option_ignore_line      = ();
525 my @option_ignore_arch_line = ();
526 my $option_all              = 0;
527 my $option_arch             = undef;
528 my $option_buildd           = 0;
529    $option_color            = 0;
530 if (not Getopt::Long::GetOptions(
531             'help|h|?'           => \$option_help,
532             'version'            => \$option_version,
533             # Hardening options.
534             'pie'                => \$option_pie,
535             'bindnow'            => \$option_bindnow,
536             'all'                => \$option_all,
537             # Ignore.
538             'ignore-arch=s'      => \@option_ignore_arch,
539             'ignore-flag=s'      => \@option_ignore_flag,
540             'ignore-arch-flag=s' => \@option_ignore_arch_flag,
541             'ignore-line=s'      => \@option_ignore_line,
542             'ignore-arch-line=s' => \@option_ignore_arch_line,
543             # Misc.
544             'color'              => \$option_color,
545             'arch=s'             => \$option_arch,
546             'buildd'             => \$option_buildd,
547         )) {
548     require Pod::Usage;
549     Pod::Usage::pod2usage(2);
550 }
551 if ($option_help) {
552     require Pod::Usage;
553     Pod::Usage::pod2usage(1);
554 }
555 if ($option_version) {
556     print "blhc $VERSION  Copyright (C) 2012  Simon Ruderich
557
558 This program is free software: you can redistribute it and/or modify
559 it under the terms of the GNU General Public License as published by
560 the Free Software Foundation, either version 3 of the License, or
561 (at your option) any later version.
562
563 This program is distributed in the hope that it will be useful,
564 but WITHOUT ANY WARRANTY; without even the implied warranty of
565 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
566 GNU General Public License for more details.
567
568 You should have received a copy of the GNU General Public License
569 along with this program.  If not, see <http://www.gnu.org/licenses/>.
570 ";
571     exit 0;
572 }
573
574 # Arguments missing.
575 if (scalar @ARGV == 0) {
576     require Pod::Usage;
577     Pod::Usage::pod2usage(2);
578 }
579
580 # Don't load Term::ANSIColor in buildd mode because Term::ANSIColor is not
581 # installed on Debian's buildds.
582 if (not $option_buildd) {
583     require Term::ANSIColor;
584 }
585
586 if ($option_all) {
587     $option_pie     = 1;
588     $option_bindnow = 1;
589 }
590
591 # Precompiled ignores for faster lookup.
592 my %option_ignore_arch_flag = ();
593 my %option_ignore_arch_line = ();
594
595 # Strip flags which should be ignored.
596 if (scalar @option_ignore_flag > 0) {
597     remove_flags(\@flag_refs, \%flag_renames, @option_ignore_flag);
598 }
599 # Same for arch specific ignore flags, but only prepare here.
600 if (scalar @option_ignore_arch_flag > 0) {
601     foreach my $ignore (@option_ignore_arch_flag) {
602         my ($ignore_arch, $ignore_flag) = split /:/, $ignore, 2;
603
604         if (not $ignore_arch or not $ignore_flag) {
605             printf STDERR 'Value "%s" invalid for option ignore-arch-flag '
606                         . '("arch:flag" expected)' . "\n", $ignore;
607             require Pod::Usage;
608             Pod::Usage::pod2usage(2);
609         }
610
611         push @{$option_ignore_arch_flag{$ignore_arch}}, $ignore_flag;
612     }
613 }
614
615 # Precompile all flag regexps. any_flags_used(), all_flags_used() get a lot
616 # faster with this.
617 foreach my $flags (@flag_refs_all) {
618     @{$flags} = compile_flag_regexp(\%flag_renames, @{$flags});
619 }
620
621 # Precompile ignore line regexps, also anchor at beginning and end of line.
622 foreach my $ignore (@option_ignore_line) {
623     $ignore = qr/^$ignore$/;
624 }
625 # Same for arch specific ignore lines.
626 if (scalar @option_ignore_arch_line > 0) {
627     foreach my $ignore (@option_ignore_arch_line) {
628         my ($ignore_arch, $ignore_line) = split /:/, $ignore, 2;
629
630         if (not $ignore_arch or not $ignore_line) {
631             printf STDERR 'Value "%s" invalid for option ignore-arch-line '
632                         . '("arch:line" expected)' . "\n", $ignore;
633             require Pod::Usage;
634             Pod::Usage::pod2usage(2);
635         }
636
637         push @{$option_ignore_arch_line{$ignore_arch}}, qr/^$ignore_line$/;
638     }
639 }
640
641 # Final exit code.
642 my $exit = 0;
643
644 FILE:
645 foreach my $file (@ARGV) {
646     print "checking '$file'...\n" if scalar @ARGV > 1;
647
648     -f $file or die "No such file: $file";
649
650     open my $fh, '<', $file or die $!;
651
652     # Architecture of this file.
653     my $arch = $option_arch;
654
655     # Hardening options. Not all architectures support all hardening options.
656     my $harden_format  = 1;
657     my $harden_fortify = 1;
658     my $harden_stack   = 1;
659     my $harden_relro   = 1;
660     my $harden_bindnow = $option_bindnow; # defaults to 0
661     my $harden_pie     = $option_pie;     # defaults to 0
662
663     # Does this build log use ada? Ada also uses gcc as compiler but uses
664     # different CFLAGS. But only perform ada checks if an ada compiler used
665     # for performance reasons.
666     my $ada = 0;
667
668     while (my $line = <$fh>) {
669         # Detect architecture automatically unless overridden. For buildd logs
670         # only, doesn't use the dpkg-buildpackage header. Necessary to ignore
671         # build logs which aren't built (wrong architecture, build error,
672         # etc.).
673         if (not $arch and index($line, 'Architecture: ') == 0) {
674             $arch = substr $line, 14, -1; # -1 to ignore '\n' at the end
675         }
676
677         # dpkg-buildflags only provides hardening flags since 1.16.1, don't
678         # check for hardening flags in buildd mode if an older dpkg-dev is
679         # used. Default flags (-g -O2) are still checked.
680         #
681         # Packages which were built before 1.16.1 but used their own hardening
682         # flags are not checked.
683         if ($option_buildd
684                 and index($line, 'Toolchain package versions: ') == 0) {
685             require Dpkg::Version;
686             if (not $line =~ /\bdpkg-dev_(\S+)/
687                     or Dpkg::Version::version_compare($1, '1.16.1') < 0) {
688                 $harden_format  = 0;
689                 $harden_fortify = 0;
690                 $harden_stack   = 0;
691                 $harden_relro   = 0;
692                 $harden_bindnow = 0;
693                 $harden_pie     = 0;
694             }
695         }
696
697         # The following two versions of CMake in Debian obeyed CPPFLAGS, but
698         # this was later dropped because upstream rejected the patch. Thus
699         # build logs with these versions will have fortify hardening flags
700         # enabled, even though they may be not correctly set and are missing
701         # when build with later CMake versions. Thanks to Aron Xu for letting
702         # me know.
703         if (index($line, 'Package versions: ') == 0
704                 and $line =~ /\bcmake_(\S+)/
705                 and ($1 eq '2.8.7-1' or $1 eq '2.8.7-2')) {
706             if (not $option_buildd) {
707                 error_invalid_cmake($1);
708                 $exit |= $exit_code{invalid_cmake};
709             } else {
710                 print "$buildd_tag{invalid_cmake}|$1|\n";
711             }
712         }
713
714         if (index($line, 'Build-Depends: ') == 0) {
715             # If hardening wrapper is used (wraps calls to gcc and adds
716             # hardening flags automatically) we can't perform any checks,
717             # abort.
718             if ($line =~ /\bhardening-wrapper\b/) {
719                 if (not $option_buildd) {
720                     error_hardening_wrapper();
721                     $exit |= $exit_code{hardening_wrapper};
722                 } else {
723                     print "$buildd_tag{hardening_wrapper}||\n";
724                 }
725                 next FILE;
726             }
727
728             # Ada compiler.
729             if ($line =~ /\bgnat\b/) {
730                 $ada = 1;
731             }
732         }
733
734         # We skip over unimportant lines at the beginning of the log to
735         # prevent false positives.
736         last if index($line, 'dpkg-buildpackage: ') == 0;
737     }
738
739     # Input lines, contain only the lines with compiler commands.
740     my @input = ();
741     # Non-verbose lines in the input. Used to reduce calls to
742     # is_non_verbose_build() (which is quite slow) in the second loop when
743     # it's already clear if a line is non-verbose or not.
744     my @input_nonverbose = ();
745
746     my $continuation = 0;
747     my $complete_line = undef;
748     while (my $line = <$fh>) {
749         # And stop at the end of the build log. Package details (reported by
750         # the buildd logs) are not important for us. This also prevents false
751         # positives.
752         last if index($line, 'Build finished at ') == 0
753                 and $line =~ /^Build finished at \d{8}-\d{4}$/;
754
755         # Detect architecture automatically unless overridden.
756         if (not $arch
757                 and index($line, 'dpkg-buildpackage: host architecture ') == 0) {
758             $arch = substr $line, 37, -1; # -1 to ignore '\n' at the end
759         }
760
761         # Ignore compiler warnings for now.
762         next if $line =~ /$warning_regex/o;
763
764         if (not $option_buildd and index($line, "\033") != -1) { # \033 = esc
765             # Remove all ANSI color sequences which are sometimes used in
766             # non-verbose builds.
767             $line = Term::ANSIColor::colorstrip($line);
768             # Also strip '\0xf' (delete previous character), used by Elinks'
769             # build system.
770             $line =~ s/\x0f//g;
771             # And "ESC(B" which seems to be used on armhf and hurd (not sure
772             # what it does).
773             $line =~ s/\033\(B//g;
774         }
775
776         # Check if this line indicates a non verbose build.
777         my $non_verbose = is_non_verbose_build($line);
778
779         # One line may contain multiple commands (";"). Treat each one as
780         # single line. parse_line() is slow, only use it when necessary.
781         my @line = (index($line, ';') == -1)
782                  ? ($line)
783                  : map {
784                        # Ensure newline at the line end - necessary for
785                        # correct parsing later.
786                        $_ =~ s/\s+$//;
787                        $_ .= "\n";
788                    } Text::ParseWords::parse_line(';', 1, $line);
789         foreach my $line (@line) {
790             if ($continuation) {
791                 $continuation = 0;
792
793                 # Join lines, but leave the "\" in place so it's clear where
794                 # the original line break was.
795                 chomp $complete_line;
796                 $complete_line .= ' ' . $line;
797             }
798             # Line continuation, line ends with "\".
799             if ($line =~ /\\$/) {
800                 $continuation = 1;
801                 # Start line continuation.
802                 if (not defined $complete_line) {
803                     $complete_line = $line;
804                 }
805                 next;
806             }
807
808             # Use the complete line if a line continuation occurred.
809             if (defined $complete_line) {
810                 $line = $complete_line;
811                 $complete_line = undef;
812             }
813
814             # Ignore lines with no compiler commands.
815             next if not $non_verbose
816                     and not $line =~ /$cc_regex_normal/o;
817             # Ignore lines with no filenames with extensions. May miss some
818             # non-verbose builds (e.g. "gcc -o test" [sic!]), but shouldn't be
819             # a problem as the log will most likely contain other non-verbose
820             # commands which are detected.
821             next if not $non_verbose
822                     and not $line =~ /$file_extension_regex/o;
823
824             # Ignore false positives.
825             #
826             # `./configure` output.
827             next if not $non_verbose
828                     and $line =~ /^(?:checking|[Cc]onfigure:) /;
829             next if $line =~ /^\s*(?:Host\s+)?(?:C(?:\+\+)?\s+)?
830                                 [Cc]ompiler[\s.]*:?\s+
831                                 /x;
832             next if $line =~ /^\s*(?:- )?(?:HOST_)?(?:CC|CXX)\s*=\s*$cc_regex_full\s*$/o;
833             # `moc-qt4`, contains '-I/usr/share/qt4/mkspecs/linux-g++' (or
834             # similar for other architectures) which gets recognized as a
835             # compiler line. Ignore it.
836             next if $line =~ m{^/usr/bin/moc-qt4
837                                \s.+\s
838                                -I/usr/share/qt4/mkspecs/[a-z]+-g\++(?:-64)?
839                                \s}x;
840             # Ignore false positives when the line contains only CC=gcc but no
841             # other gcc command.
842             if ($line =~ /(.*)CC=$cc_regex_full(.*)/o) {
843                 my $before = $1;
844                 my $after  = $2;
845                 next if     not $before =~ /$cc_regex_normal/o
846                         and not $after  =~ /$cc_regex_normal/o;
847             }
848
849             # Check if additional hardening options were used. Used to ensure
850             # they are used for the complete build.
851             $harden_pie     = 1 if any_flags_used($line, @def_cflags_pie,
852                                                          @def_ldflags_pie);
853             $harden_bindnow = 1 if any_flags_used($line, @def_ldflags_bindnow);
854
855             push @input, $line;
856             push @input_nonverbose, $non_verbose;
857         }
858     }
859
860     close $fh or die $!;
861
862     # Ignore arch if requested.
863     if (scalar @option_ignore_arch > 0 and $arch) {
864         foreach my $ignore (@option_ignore_arch) {
865             if ($arch eq $ignore) {
866                 print "ignoring architecture '$arch'\n";
867                 next FILE;
868             }
869         }
870     }
871
872     if (scalar @input == 0) {
873         if (not $option_buildd) {
874             print "No compiler commands!\n";
875             $exit |= $exit_code{no_compiler_commands};
876         } else {
877             print "$buildd_tag{no_compiler_commands}||\n";
878         }
879         next FILE;
880     }
881
882     if ($option_buildd) {
883         $statistics{commands} += scalar @input;
884     }
885
886     # Option or auto detected.
887     if ($arch) {
888         # The following was partially copied from dpkg-dev 1.16.4.3
889         # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, add_hardening_flags()),
890         # copyright Raphaël Hertzog <hertzog@debian.org>, Kees Cook
891         # <kees@debian.org>, Canonical, Ltd. licensed under GPL version 2 or
892         # later. Keep it in sync.
893
894         require Dpkg::Arch;
895         my ($abi, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch);
896
897         # Disable unsupported hardening options.
898         if ($cpu =~ /^(?:ia64|alpha|mips|mipsel|hppa)$/ or $arch eq 'arm') {
899             $harden_stack = 0;
900         }
901         if ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
902             $harden_relro   = 0;
903             $harden_bindnow = 0;
904         }
905     }
906
907     # Default values.
908     my @cflags   = @def_cflags;
909     my @cxxflags = @def_cxxflags;
910     my @cppflags = @def_cppflags;
911     my @ldflags  = @def_ldflags;
912     # Check the specified hardening options, same order as dpkg-buildflags.
913     if ($harden_pie) {
914         @cflags   = (@cflags,   @def_cflags_pie);
915         @cxxflags = (@cxxflags, @def_cflags_pie);
916         @ldflags  = (@ldflags,  @def_ldflags_pie);
917     }
918     if ($harden_stack) {
919         @cflags   = (@cflags,   @def_cflags_stack);
920         @cxxflags = (@cxxflags, @def_cflags_stack);
921     }
922     if ($harden_fortify) {
923         @cflags   = (@cflags,   @def_cflags_fortify);
924         @cxxflags = (@cxxflags, @def_cflags_fortify);
925         @cppflags = (@cppflags, @def_cppflags_fortify);
926     }
927     if ($harden_format) {
928         @cflags   = (@cflags,   @def_cflags_format);
929         @cxxflags = (@cxxflags, @def_cflags_format);
930     }
931     if ($harden_relro) {
932         @ldflags = (@ldflags, @def_ldflags_relro);
933     }
934     if ($harden_bindnow) {
935         @ldflags = (@ldflags, @def_ldflags_bindnow);
936     }
937
938     # Stores normal CFLAGS when @cflags_ada are temporarily used.
939     my @cflags_backup;
940     # Ada CFLAGS.
941     my @cflags_ada = @cflags;
942     # Ada doesn't support format hardening flags, see #680117 for more
943     # information. Filter them out if ada is used.
944     if ($ada and $harden_format) {
945         @cflags_ada = grep {
946             my $ok = 1;
947             foreach my $flag (@def_cflags_format) {
948                 $ok = 0 if $_ eq $flag;
949             }
950             $ok;
951         } @cflags;
952     }
953
954     # Hack to fix cppflags_fortify_broken() if --ignore-flag
955     # -D_FORTIFY_SOURCE=2 is used to ignore missing fortification. Only works
956     # as long as @def_cppflags_fortify contains only one variable.
957     if (scalar @def_cppflags_fortify == 0) {
958         $harden_fortify = 0;
959     }
960
961     # Ignore flags for this arch if requested.
962     if ($arch and exists $option_ignore_arch_flag{$arch}) {
963         my @local_flag_refs = (\@cflags, \@cxxflags, \@cppflags, \@ldflags);
964
965         remove_flags(\@local_flag_refs,
966                      \%flag_renames,
967                      @{$option_ignore_arch_flag{$arch}});
968     }
969
970     my @ignore_line = @option_ignore_line;
971     # Ignore lines for this arch if requested.
972     if ($arch and exists $option_ignore_arch_line{$arch}) {
973         @ignore_line = (@ignore_line, @{$option_ignore_arch_line{$arch}});
974     }
975
976 LINE:
977     for (my $i = 0; $i < scalar @input; $i++) {
978         my $line = $input[$i];
979
980         # Ignore line if requested.
981         foreach my $ignore (@ignore_line) {
982             next LINE if $line =~ /$ignore/;
983         }
984
985         my $skip = 0;
986         if ($input_nonverbose[$i]
987                 and is_non_verbose_build($line, $input[$i + 1], \$skip)) {
988             if (not $option_buildd) {
989                 error_non_verbose_build($line);
990                 $exit |= $exit_code{non_verbose_build};
991             } else {
992                 $statistics{commands_nonverbose}++;
993             }
994             next;
995         }
996         # Even if it's a verbose build, we might have to skip this line.
997         next if $skip;
998
999         # Remove everything until and including the compiler command. Makes
1000         # checks easier and faster.
1001         $line =~ s/^.*?$cc_regex//o;
1002         # "([...] test.c)" is not detected as 'test.c' - fix this by removing
1003         # the brace and similar characters at the line end.
1004         $line =~ s/['")]+$//;
1005
1006         # Skip unnecessary tests when only preprocessing.
1007         my $flag_preprocess = 0;
1008
1009         my $dependency = 0;
1010         my $preprocess = 0;
1011         my $compile    = 0;
1012         my $link       = 0;
1013
1014         # Preprocess, compile, assemble.
1015         if ($line =~ /\s(-E|-S|-c)\b/) {
1016             $preprocess      = 1;
1017             $flag_preprocess = 1 if $1 eq '-E';
1018             $compile         = 1 if $1 eq '-S' or $1 eq '-c';
1019         # Dependency generation for Makefiles. The other flags (-MF -MG -MP
1020         # -MT -MQ) are always used with -M/-MM.
1021         } elsif ($line =~ /\s(?:-M|-MM)\b/) {
1022             $dependency = 1;
1023         # Otherwise assume we are linking.
1024         } else {
1025             $link = 1;
1026         }
1027
1028         # -MD/-MMD also cause dependency generation, but they don't imply -E!
1029         if ($line =~ /\s(?:-MD|-MMD)\b/) {
1030             $dependency      = 0;
1031             $flag_preprocess = 0;
1032         }
1033
1034         # Dependency generation for Makefiles, no preprocessing or other flags
1035         # needed.
1036         next if $dependency;
1037
1038         # Get all file extensions on this line.
1039         my @extensions = $line =~ /$file_extension_regex/go;
1040         # Ignore all unknown extensions to speedup the search below.
1041         @extensions = grep { exists $extension{$_} } @extensions;
1042
1043         # These file types don't require preprocessing.
1044         if (extension_found(\%extensions_no_preprocess, @extensions)) {
1045             $preprocess = 0;
1046         }
1047         # These file types require preprocessing.
1048         if (extension_found(\%extensions_preprocess, @extensions)) {
1049             $preprocess = 1;
1050         }
1051
1052         if (not $flag_preprocess) {
1053             # If there are source files then it's compiling/linking in one
1054             # step and we must check both. We only check for source files
1055             # here, because header files cause too many false positives.
1056             if (extension_found(\%extensions_compile_link, @extensions)) {
1057                 # Assembly files don't need CFLAGS.
1058                 if (not extension_found(\%extensions_compile, @extensions)
1059                         and extension_found(\%extensions_no_compile, @extensions)) {
1060                     $compile = 0;
1061                 # But the rest does.
1062                 } else {
1063                     $compile = 1;
1064                 }
1065             # No compilable extensions found, either linking or compiling
1066             # header flags.
1067             #
1068             # If there are also no object files we are just compiling headers
1069             # (.h -> .h.gch). Don't check for linker flags in this case. Due
1070             # to our liberal checks for compiler lines, this also reduces the
1071             # number of false positives considerably.
1072             } elsif ($link
1073                     and not extension_found(\%extensions_object, @extensions)) {
1074                 $link = 0;
1075             }
1076         }
1077
1078         my $compile_cpp = 0;
1079         my $compile_ada = 0;
1080         # Assume CXXFLAGS are required when a C++ file is specified in the
1081         # compiler line.
1082         if ($compile
1083                 and extension_found(\%extensions_compile_cpp, @extensions)) {
1084             $compile     = 0;
1085             $compile_cpp = 1;
1086         # Ada needs special CFLAGS, use them if only ada files are compiled.
1087         } elsif ($ada
1088                     and $compile
1089                     and array_equal(\@extensions,
1090                                     \@source_no_preprocess_compile_ada)) {
1091             $compile_ada = 1;
1092             @cflags_backup = @cflags;
1093             @cflags        = @cflags_ada;
1094         }
1095
1096         if ($option_buildd) {
1097             $statistics{preprocess}++  if $preprocess;
1098             $statistics{compile}++     if $compile;
1099             $statistics{compile_cpp}++ if $compile_cpp;
1100             $statistics{link}++        if $link;
1101         }
1102
1103         # Check hardening flags.
1104         my @missing;
1105         if ($compile and not all_flags_used($line, \@missing, @cflags)
1106                 # Libraries linked with -fPIC don't have to (and can't) be
1107                 # linked with -fPIE as well. It's no error if only PIE flags
1108                 # are missing.
1109                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1110                 # Assume dpkg-buildflags returns the correct flags.
1111                 and index($line, '`dpkg-buildflags --get CFLAGS`') == -1) {
1112             if (not $option_buildd) {
1113                 error_flags('CFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1114                 $exit |= $exit_code{flags_missing};
1115             } else {
1116                 $statistics{compile_missing}++;
1117             }
1118         } elsif ($compile_cpp and not all_flags_used($line, \@missing, @cflags)
1119                 # Libraries linked with -fPIC don't have to (and can't) be
1120                 # linked with -fPIE as well. It's no error if only PIE flags
1121                 # are missing.
1122                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1123                 # Assume dpkg-buildflags returns the correct flags.
1124                 and index($line, '`dpkg-buildflags --get CXXFLAGS`') == -1) {
1125             if (not $option_buildd) {
1126                 error_flags('CXXFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1127                 $exit |= $exit_code{flags_missing};
1128             } else {
1129                 $statistics{compile_cpp_missing}++;
1130             }
1131         }
1132         if ($preprocess
1133                 and (not all_flags_used($line, \@missing, @cppflags)
1134                     # The fortify flag might be overwritten, detect that.
1135                      or ($harden_fortify
1136                          and cppflags_fortify_broken($line, \@missing)))
1137                 # Assume dpkg-buildflags returns the correct flags.
1138                 and index($line, '`dpkg-buildflags --get CPPFLAGS`') == -1) {
1139             if (not $option_buildd) {
1140                 error_flags('CPPFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1141                 $exit |= $exit_code{flags_missing};
1142             } else {
1143                 $statistics{preprocess_missing}++;
1144             }
1145         }
1146         if ($link and not all_flags_used($line, \@missing, @ldflags)
1147                 # Same here, -fPIC conflicts with -fPIE.
1148                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_ldflags_pie)
1149                 # Assume dpkg-buildflags returns the correct flags.
1150                 and index($line, '`dpkg-buildflags --get LDFLAGS`') == -1) {
1151             if (not $option_buildd) {
1152                 error_flags('LDFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1153                 $exit |= $exit_code{flags_missing};
1154             } else {
1155                 $statistics{link_missing}++;
1156             }
1157         }
1158
1159         # Restore normal CFLAGS.
1160         if ($compile_ada) {
1161             @cflags = @cflags_backup;
1162         }
1163     }
1164 }
1165
1166 # Print statistics for buildd mode, only output in this mode.
1167 if ($option_buildd) {
1168     my @warning;
1169
1170     if ($statistics{preprocess_missing}) {
1171         push @warning, sprintf 'CPPFLAGS %d (of %d)',
1172                                $statistics{preprocess_missing},
1173                                $statistics{preprocess};
1174     }
1175     if ($statistics{compile_missing}) {
1176         push @warning, sprintf 'CFLAGS %d (of %d)',
1177                                $statistics{compile_missing},
1178                                $statistics{compile};
1179     }
1180     if ($statistics{compile_cpp_missing}) {
1181         push @warning, sprintf 'CXXFLAGS %d (of %d)',
1182                                $statistics{compile_cpp_missing},
1183                                $statistics{compile_cpp};
1184     }
1185     if ($statistics{link_missing}) {
1186         push @warning, sprintf 'LDFLAGS %d (of %d)',
1187                                $statistics{link_missing},
1188                                $statistics{link};
1189     }
1190     if (scalar @warning) {
1191         local $" = ', '; # array join string
1192         print "$buildd_tag{flags_missing}|@warning missing|\n";
1193     }
1194
1195     if ($statistics{commands_nonverbose}) {
1196         printf "$buildd_tag{non_verbose_build}|%d (of %d) hidden|\n",
1197                $statistics{commands_nonverbose},
1198                $statistics{commands},
1199     }
1200 }
1201
1202
1203 exit $exit;
1204
1205
1206 __END__
1207
1208 =head1 NAME
1209
1210 blhc - build log hardening check, checks build logs for missing hardening flags
1211
1212 =head1 SYNOPSIS
1213
1214 B<blhc> [I<options>] I<< <dpkg-buildpackage build log file>.. >>
1215
1216 =head1 DESCRIPTION
1217
1218 blhc is a small tool which checks build logs for missing hardening flags. It's
1219 licensed under the GPL 3 or later.
1220
1221 It's designed to check build logs generated by Debian's dpkg-buildpackage (or
1222 tools using dpkg-buildpackage like pbuilder or the official buildd build logs)
1223 to help maintainers detect missing hardening flags in their packages.
1224
1225 Only gcc is detected as compiler at the moment. If other compilers support
1226 hardening flags as well, please report them.
1227
1228 If there's no output, no flags are missing and the build log is fine.
1229
1230 =head1 OPTIONS
1231
1232 =over 8
1233
1234 =item B<--all>
1235
1236 Force check for all +all (+pie, +bindnow) hardening flags. By default it's
1237 auto detected.
1238
1239 =item B<--arch> I<architecture>
1240
1241 Set the specific architecture (e.g. amd64, armel, etc.), automatically
1242 disables hardening flags not available on this architecture. Is detected
1243 automatically if dpkg-buildpackage is used.
1244
1245 =item B<--bindnow>
1246
1247 Force check for all +bindnow hardening flags. By default it's auto detected.
1248
1249 =item B<--buildd>
1250
1251 Special mode for buildds when automatically parsing log files. The following
1252 changes are in effect:
1253
1254 =over 2
1255
1256 =item *
1257
1258 Print tags instead of normal warnings, see L</"BUILDD TAGS"> for a list of
1259 possible tags.
1260
1261 =item *
1262
1263 Don't check hardening flags in old log files (if dpkg-dev << 1.16.1 is
1264 detected).
1265
1266 =item *
1267
1268 Don't require Term::ANSIColor.
1269
1270 =item *
1271
1272 Return exit code 0, unless there was a error (-I, -W messages don't count as
1273 error).
1274
1275 =back
1276
1277 =item B<--color>
1278
1279 Use colored (ANSI) output for warning messages.
1280
1281 =item B<--ignore-arch> I<arch>
1282
1283 Ignore build logs from architectures matching I<arch>. I<arch> is a string.
1284
1285 Used to prevent false positives. This option can be specified multiple times.
1286
1287 =item B<--ignore-arch-flag> I<arch>:I<flag>
1288
1289 Like B<--ignore-flag>, but only ignore flag on I<arch>.
1290
1291 =item B<--ignore-arch-line> I<arch>:I<line>
1292
1293 Like B<--ignore-line>, but only ignore line on I<arch>.
1294
1295 =item B<--ignore-flag> I<flag>
1296
1297 Don't print an error when the specific flag is missing in a compiler line.
1298 I<flag> is a string.
1299
1300 Used to prevent false positives. This option can be specified multiple times.
1301
1302 =item B<--ignore-line> I<regex>
1303
1304 Ignore lines matching the given Perl regex. I<regex> is automatically anchored
1305 at the beginning and end of the line to prevent false negatives.
1306
1307 B<NOTE>: Not the input lines are checked, but the lines which are displayed in
1308 warnings (which have line continuation resolved).
1309
1310 Used to prevent false positives. This option can be specified multiple times.
1311
1312 =item B<--pie>
1313
1314 Force check for all +pie hardening flags. By default it's auto detected.
1315
1316 =item B<-h -? --help>
1317
1318 Print available options.
1319
1320 =item B<--version>
1321
1322 Print version number and license.
1323
1324 =back
1325
1326 Auto detection for B<--pie> and B<--bindnow> only works if at least one
1327 command uses the required hardening flag (e.g. -fPIE). Then it's required for
1328 all other commands as well.
1329
1330 =head1 EXAMPLES
1331
1332 Normal usage, parse a single log file.
1333
1334     blhc path/to/log/file
1335
1336 If there's no output, no flags are missing and the build log is fine.
1337
1338 Parse multiple log files. The exit code is ORed over all files.
1339
1340     blhc path/to/directory/with/log/files/*
1341
1342 Don't treat missing C<-g> as error:
1343
1344     blhc --ignore-flag -g path/to/log/file
1345
1346 Don't treat missing C<-pie> on kfreebsd-amd64 as error:
1347
1348     blhc --ignore-arch-flag kfreebsd-amd64:-pie path/to/log/file
1349
1350 Ignore lines consisting exactly of C<./script gcc file> which would cause a
1351 false positive.
1352
1353     blhc --ignore-line '\./script gcc file' path/to/log/file
1354
1355 Ignore lines matching C<./script gcc file> somewhere in the line.
1356
1357     blhc --ignore-line '.*\./script gcc file.*' path/to/log/file
1358
1359 Use blhc with pbuilder.
1360
1361     pbuilder path/to/package.dsc | tee path/log/file
1362     blhc path/to/file || echo flags missing
1363
1364 =head1 BUILDD TAGS
1365
1366 The following tags are used in I<--buildd> mode. In braces the additional data
1367 which is displayed.
1368
1369 =over 2
1370
1371 =item B<I-hardening-wrapper-used>
1372
1373 The package uses hardening-wrapper which intercepts calls to gcc and adds
1374 hardening flags. The build log doesn't contain any hardening flags and thus
1375 can't be checked by blhc.
1376
1377 =item B<W-compiler-flags-hidden> (summary of hidden lines)
1378
1379 Build log contains lines which hide the real compiler flags. For example:
1380
1381     CC test-a.c
1382     CC test-b.c
1383     CC test-c.c
1384     LD test
1385
1386 Most of the time either C<export V=1> or C<export verbose=1> in
1387 F<debian/rules> fixes builds with hidden compiler flags. Sometimes C<.SILENT>
1388 in a F<Makefile> must be removed. And as last resort the F<Makefile> must be
1389 patched to remove the C<@>s hiding the real compiler commands.
1390
1391 =item B<W-dpkg-buildflags-missing> (summary of missing flags)
1392
1393 CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS missing.
1394
1395 =item B<I-invalid-cmake-used> (version)
1396
1397 By default CMake ignores CPPFLAGS thus missing those hardening flags. Debian
1398 patched CMake in versions 2.8.7-1 and 2.8.7-2 to respect CPPFLAGS, but this
1399 patch was rejected by upstream and later reverted in Debian. Thus those two
1400 versions show correct usage of CPPFLAGS even if the package doesn't correctly
1401 handle them (for example by passing them to CFLAGS). To prevent false
1402 negatives just blacklist those two versions.
1403
1404 =item B<I-no-compiler-commands>
1405
1406 No compiler commands were detected. Either the log contains none or they were
1407 not correctly detected by blhc (please report the bug in this case).
1408
1409 =back
1410
1411 =head1 EXIT STATUS
1412
1413 The exit status is a "bit mask", each listed status is ORed when the error
1414 condition occurs to get the result.
1415
1416 =over 4
1417
1418 =item B<0>
1419
1420 Success.
1421
1422 =item B<1>
1423
1424 No compiler commands were found.
1425
1426 =item B<2>
1427
1428 Invalid arguments/options given to blhc.
1429
1430 =item B<4>
1431
1432 Non verbose build.
1433
1434 =item B<8>
1435
1436 Missing hardening flags.
1437
1438 =item B<16>
1439
1440 Hardening wrapper detected, no tests performed.
1441
1442 =item B<32>
1443
1444 Invalid CMake version used. See B<I-invalid-cmake-used> under L</"BUILDD
1445 TAGS"> for a detailed explanation.
1446
1447 =back
1448
1449 =head1 AUTHOR
1450
1451 Simon Ruderich, E<lt>simon@ruderich.orgE<gt>
1452
1453 Thanks to to Bernhard R. Link E<lt>brlink@debian.orgE<gt> and Jaria Alto
1454 E<lt>jari.aalto@cante.netE<gt> for their valuable input and suggestions.
1455
1456 =head1 LICENSE AND COPYRIGHT
1457
1458 Copyright (C) 2012 by Simon Ruderich
1459
1460 This program is free software: you can redistribute it and/or modify
1461 it under the terms of the GNU General Public License as published by
1462 the Free Software Foundation, either version 3 of the License, or
1463 (at your option) any later version.
1464
1465 This program is distributed in the hope that it will be useful,
1466 but WITHOUT ANY WARRANTY; without even the implied warranty of
1467 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1468 GNU General Public License for more details.
1469
1470 You should have received a copy of the GNU General Public License
1471 along with this program.  If not, see <http://www.gnu.org/licenses/>.
1472
1473 =head1 SEE ALSO
1474
1475 L<hardening-check(1)>, L<dpkg-buildflags(1)>
1476
1477 =cut