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