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