File Coverage

File:Dpkg/Vendor/Debian.pm
Coverage:65.2%

linestmtbrancondsubpodtimecode
1# Copyright © 2009-2011 Raphaël Hertzog <hertzog@debian.org>
2# Copyright © 2009-2024 Guillem Jover <guillem@debian.org>
3#
4# Hardening build flags handling derived from work of:
5# Copyright © 2009-2011 Kees Cook <kees@debian.org>
6# Copyright © 2007-2008 Canonical, Ltd.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
21=encoding utf8
22
23 - 34
=head1 NAME

Dpkg::Vendor::Debian - Debian vendor class

=head1 DESCRIPTION

This vendor class customizes the behavior of dpkg scripts for Debian
specific behavior and policies.

B<Note>: This is a private module, its API can change at any time.

=cut
35
36package Dpkg::Vendor::Debian 0.01;
37
38
60
60
60
1380
42
1032
use strict;
39
60
60
60
110
35
1681
use warnings;
40
41
60
60
60
125
55
1930
use List::Util qw(any none);
42
43
60
60
60
148
46
959
use Dpkg;
44
60
60
60
114
36
1567
use Dpkg::Gettext;
45
60
60
60
122
36
1926
use Dpkg::ErrorHandling;
46
60
60
60
119
44
2805
use Dpkg::Control::Types;
47
48
60
60
60
123
53
203
use parent qw(Dpkg::Vendor::Default);
49
50sub run_hook {
51
891
1
1053
    my ($self, $hook, @params) = @_;
52
53
891
3676
    if ($hook eq 'package-keyrings') {
54
0
0
        return ('/usr/share/keyrings/debian-keyring.gpg',
55                '/usr/share/keyrings/debian-nonupload.gpg',
56                '/usr/share/keyrings/debian-maintainers.gpg');
57    } elsif ($hook eq 'archive-keyrings') {
58
0
0
        return ('/usr/share/keyrings/debian-archive-keyring.gpg');
59    } elsif ($hook eq 'archive-keyrings-historic') {
60
0
0
        return ('/usr/share/keyrings/debian-archive-removed-keys.gpg');
61    } elsif ($hook eq 'builtin-build-depends') {
62
0
0
        return qw(build-essential:native);
63    } elsif ($hook eq 'builtin-build-conflicts') {
64
0
0
        return ();
65    } elsif ($hook eq 'register-custom-fields') {
66    } elsif ($hook eq 'extend-patch-header') {
67
0
0
        my ($textref, $ch_info) = @params;
68
0
0
        if ($ch_info->{'Closes'}) {
69
0
0
            foreach my $bug (split(/\s+/, $ch_info->{'Closes'})) {
70
0
0
                $$textref .= "Bug-Debian: https://bugs.debian.org/$bug\n";
71            }
72        }
73
74        # XXX: Layer violation...
75
0
0
        require Dpkg::Vendor::Ubuntu;
76
0
0
        my $b = Dpkg::Vendor::Ubuntu::find_launchpad_closes($ch_info->{'Changes'});
77
0
0
        foreach my $bug (@$b) {
78
0
0
            $$textref .= "Bug-Ubuntu: https://bugs.launchpad.net/bugs/$bug\n";
79        }
80    } elsif ($hook eq 'update-buildflags') {
81
81
168
        $self->set_build_features(@params);
82
81
201
        $self->add_build_flags(@params);
83    } elsif ($hook eq 'builtin-system-build-paths') {
84
0
0
        return qw(/build/);
85    } elsif ($hook eq 'build-tainted-by') {
86
0
0
        return $self->_build_tainted_by();
87    } elsif ($hook eq 'sanitize-environment') {
88        # Reset umask to a sane default.
89
0
0
        umask 0022;
90        # Reset locale to a sane default.
91        #
92        # We ignore the LANGUAGE GNU extension, as that only affects
93        # LC_MESSAGES which will use LC_CTYPE for its codeset. We need to
94        # move the high priority LC_ALL catch-all into the low-priority
95        # LANG catch-all so that we can override LC_* variables, and remove
96        # any existing LC_* variables which would have been ignored anyway,
97        # and would now take precedence over LANG.
98
0
0
        if (length $ENV{LC_ALL}) {
99
0
0
            $ENV{LANG} = delete $ENV{LC_ALL};
100
0
0
0
0
            foreach my $lc (grep { m/^LC_/ } keys %ENV) {
101
0
0
                delete $ENV{$lc};
102            }
103        }
104
0
0
        $ENV{LC_COLLATE} = 'C.UTF-8';
105
0
0
        $ENV{LC_CTYPE} = 'C.UTF-8';
106    } elsif ($hook eq 'backport-version-regex') {
107
12
41
        return qr/~(bpo|deb)/;
108    } else {
109
750
1123
        return $self->SUPER::run_hook($hook, @params);
110    }
111}
112
113sub init_build_features {
114
81
0
128
    my ($self, $use_feature, $builtin_feature) = @_;
115}
116
117sub set_build_features {
118
81
1
94
    my ($self, $flags) = @_;
119
120    # Default feature states.
121
81
904
    my %use_feature = (
122        future => {
123            # XXX: Should start a deprecation cycle at some point.
124            lfs => 0,
125        },
126        abi => {
127            # XXX: This is set to undef so that we can handle the alias from
128            # the future feature area.
129            lfs => undef,
130            # XXX: This is set to undef to handle mask on the default setting.
131            time64 => undef,
132        },
133        qa => {
134            bug => undef,
135            'bug-implicit-func' => undef,
136            canary => 0,
137        },
138        reproducible => {
139            timeless => 1,
140            fixfilepath => 1,
141            fixdebugpath => 1,
142        },
143        optimize => {
144            lto => 0,
145        },
146        sanitize => {
147            address => 0,
148            thread => 0,
149            leak => 0,
150            undefined => 0,
151        },
152        hardening => {
153            # XXX: This is set to undef so that we can cope with the brokenness
154            # of gcc managing this feature builtin.
155            pie => undef,
156            stackprotector => 1,
157            stackprotectorstrong => 1,
158            stackclash => 1,
159            fortify => 1,
160            format => 1,
161            relro => 1,
162            bindnow => 0,
163            branch => 1,
164        },
165    );
166
167
81
205
    my %builtin_feature = (
168        abi => {
169            lfs => 0,
170            time64 => 0,
171        },
172        hardening => {
173            pie => 1,
174        },
175    );
176
177
81
3660
    require Dpkg::Arch;
178
179
81
159
    my $arch = Dpkg::Arch::get_host_arch();
180
81
175
    my ($abi, $libc, $os, $cpu) = Dpkg::Arch::debarch_to_debtuple($arch);
181
81
161
    my ($abi_bits, $abi_endian) = Dpkg::Arch::debarch_to_abiattrs($arch);
182
183
81
447
    unless (defined $abi and defined $libc and defined $os and defined $cpu) {
184
0
0
        warning(g_("unknown host architecture '%s'"), $arch);
185
0
0
        ($abi, $os, $cpu) = ('', '', '');
186    }
187
81
215
    unless (defined $abi_bits and defined $abi_endian) {
188
0
0
        warning(g_("unknown abi attributes for architecture '%s'"), $arch);
189
0
0
        ($abi_bits, $abi_endian) = (0, 'unknown');
190    }
191
192    # Mask builtin features that are not enabled by default in the compiler.
193
81
2349
160
2218
    my %builtin_pie_arch = map { $_ => 1 } qw(
194        amd64
195        arm64
196        armel
197        armhf
198        hurd-amd64
199        hurd-i386
200        i386
201        kfreebsd-amd64
202        kfreebsd-i386
203        loong64
204        mips
205        mips64
206        mips64el
207        mips64r6
208        mips64r6el
209        mipsel
210        mipsn32
211        mipsn32el
212        mipsn32r6
213        mipsn32r6el
214        mipsr6
215        mipsr6el
216        powerpc
217        ppc64
218        ppc64el
219        riscv64
220        s390x
221        sparc
222        sparc64
223    );
224
81
200
    if (not exists $builtin_pie_arch{$arch}) {
225
0
0
        $builtin_feature{hardening}{pie} = 0;
226    }
227
228
81
156
    if ($abi_bits != 32) {
229
42
57
        $builtin_feature{abi}{lfs} = 1;
230    }
231
232    # On glibc, new ports default to time64, old ports currently default
233    # to time32, so we track the latter as that is a list that is not
234    # going to grow further, and might shrink.
235    # On musl libc based systems all ports use time64.
236
81
2187
130
1830
    my %time32_arch = map { $_ => 1 } qw(
237        arm
238        armeb
239        armel
240        armhf
241        hppa
242        i386
243        hurd-i386
244        kfreebsd-i386
245        m68k
246        mips
247        mipsel
248        mipsn32
249        mipsn32el
250        mipsn32r6
251        mipsn32r6el
252        mipsr6
253        mipsr6el
254        nios2
255        powerpc
256        powerpcel
257        powerpcspe
258        s390
259        sh3
260        sh3eb
261        sh4
262        sh4eb
263        sparc
264    );
265
81
368
    if ($abi_bits != 32 or
266        not exists $time32_arch{$arch} or
267        $libc eq 'musl') {
268
42
49
        $builtin_feature{abi}{time64} = 1;
269    }
270
271
81
229
    $self->init_build_features(\%use_feature, \%builtin_feature);
272
273    ## Setup
274
275
81
3026
    require Dpkg::BuildOptions;
276
277    # Adjust features based on user or maintainer's desires.
278
81
267
    my $opts_build = Dpkg::BuildOptions->new(envvar => 'DEB_BUILD_OPTIONS');
279
81
120
    my $opts_maint = Dpkg::BuildOptions->new(envvar => 'DEB_BUILD_MAINT_OPTIONS');
280
281
81
356
    foreach my $area (sort keys %use_feature) {
282
567
689
        $opts_build->parse_features($area, $use_feature{$area});
283
567
568
        $opts_maint->parse_features($area, $use_feature{$area});
284    }
285
286    ## Area: abi
287
288
81
153
299
267
    if (any { $arch eq $_ } qw(hurd-i386 kfreebsd-i386)) {
289        # Mask time64 on hurd-i386 and kfreebsd-i386, as their kernel lacks
290        # support for that arch and it will not be implemented.
291
9
10
        $use_feature{abi}{time64} = 0;
292    } elsif (not defined $use_feature{abi}{time64}) {
293        # If the user has not requested a specific setting, by default only
294        # enable time64 everywhere except for i386, where we preserve it for
295        # binary backwards compatibility.
296
54
62
        if ($arch eq 'i386') {
297
15
26
            $use_feature{abi}{time64} = 0;
298        } else {
299
39
45
            $use_feature{abi}{time64} = 1;
300        }
301    }
302
303    # In Debian gcc enables time64 (and lfs) for the following architectures
304    # by injecting pre-processor flags, though the libc ABI has not changed.
305
81
594
212
371
    if (any { $arch eq $_ } qw(armel armhf hppa m68k mips mipsel powerpc sh4)) {
306
9
23
        $flags->set_option_value('cc-abi-time64', 1);
307    } else {
308
72
150
        $flags->set_option_value('cc-abi-time64', 0);
309    }
310
311
81
245
    if ($use_feature{abi}{time64} && ! $builtin_feature{abi}{time64}) {
312        # On glibc 64-bit time_t support requires LFS.
313
9
22
        $use_feature{abi}{lfs} = 1 if $libc eq 'gnu';
314    }
315
316    # XXX: Handle lfs alias from future abi feature area.
317
81
264
    $use_feature{abi}{lfs} //= $use_feature{future}{lfs};
318    # XXX: Once the feature is set in the abi area, we always override the
319    # one in the future area.
320
81
79
    $use_feature{future}{lfs} = $use_feature{abi}{lfs};
321
322    ## Area: qa
323
324    # For time64 we require -Werror=implicit-function-declaration, to avoid
325    # linking against the wrong symbol. Instead of enabling this conditionally
326    # on time64 being enabled, do it unconditionally so that the effects are
327    # uniform and visible on all architectures. Unless it has been set
328    # explicitly.
329
81
343
    $use_feature{qa}{'bug-implicit-func'} //= $use_feature{qa}{bug} // 1;
330
331
81
252
    $use_feature{qa}{bug} //= 0;
332
333    ## Area: reproducible
334
335    # Mask features that might have an unsafe usage.
336
81
149
    if ($use_feature{reproducible}{fixfilepath} or
337        $use_feature{reproducible}{fixdebugpath}) {
338
81
244
        require Cwd;
339
340
81
563
        my $build_path =$ENV{DEB_BUILD_PATH} || Cwd::getcwd();
341
342
81
142
        $flags->set_option_value('build-path', $build_path);
343
344        # If we have any unsafe character in the path, disable the flag,
345        # so that we do not need to worry about escaping the characters
346        # on output.
347
81
200
        if ($build_path =~ m/[^-+:.0-9a-zA-Z~\/_]/) {
348
0
0
            $use_feature{reproducible}{fixfilepath} = 0;
349
0
0
            $use_feature{reproducible}{fixdebugpath} = 0;
350        }
351    }
352
353    ## Area: optimize
354
355
81
122
    if ($opts_build->has('noopt')) {
356
0
0
        $flags->set_option_value('optimize-level', 0);
357    } else {
358
81
91
        $flags->set_option_value('optimize-level', 2);
359    }
360
361    ## Area: sanitize
362
363    # Handle logical feature interactions.
364
81
129
    if ($use_feature{sanitize}{address} and $use_feature{sanitize}{thread}) {
365        # Disable the thread sanitizer when the address one is active, they
366        # are mutually incompatible.
367
0
0
        $use_feature{sanitize}{thread} = 0;
368    }
369
81
241
    if ($use_feature{sanitize}{address} or $use_feature{sanitize}{thread}) {
370        # Disable leak sanitizer, it is implied by the address or thread ones.
371
0
0
        $use_feature{sanitize}{leak} = 0;
372    }
373
374    ## Area: hardening
375
376    # Mask features that are not available on certain architectures.
377
81
99
189
272
    if (none { $os eq $_ } qw(linux kfreebsd hurd) or
378
243
259
        any { $cpu eq $_ } qw(alpha hppa ia64)) {
379        # Disabled on non-(linux/kfreebsd/hurd).
380        # Disabled on alpha, hppa, ia64.
381
0
0
        $use_feature{hardening}{pie} = 0;
382    }
383
81
324
247
379
    if (any { $cpu eq $_ } qw(ia64 alpha hppa nios2) or $arch eq 'arm') {
384        # Stack protector disabled on ia64, alpha, hppa, nios2.
385        #   "warning: -fstack-protector not supported for this target"
386        # Stack protector disabled on arm (ok on armel).
387        #   compiler supports it incorrectly (leads to SEGV)
388
0
0
        $use_feature{hardening}{stackprotector} = 0;
389    }
390
81
216
191
173
    if (none { $arch eq $_ } qw(amd64 arm64 armhf armel)) {
391        # Stack clash protector only available on amd64 and arm.
392
39
45
        $use_feature{hardening}{stackclash} = 0;
393    }
394
81
162
161
231
    if (any { $cpu eq $_ } qw(ia64 hppa)) {
395        # relro not implemented on ia64, hppa.
396
0
0
        $use_feature{hardening}{relro} = 0;
397    }
398
81
129
149
132
    if (none { $cpu eq $_ } qw(amd64 arm64)) {
399        # On amd64 use -fcf-protection.
400        # On arm64 use -mbranch-protection=standard.
401
48
53
        $use_feature{hardening}{branch} = 0;
402    }
403
81
141
    $flags->set_option_value('hardening-branch-cpu', $cpu);
404
405    # Mask features that might be influenced by other flags.
406
81
126
    if ($flags->get_option_value('optimize-level') == 0) {
407      # glibc 2.16 and later warn when using -O0 and _FORTIFY_SOURCE.
408
0
0
      $use_feature{hardening}{fortify} = 0;
409    }
410
81
120
    $flags->set_option_value('fortify-level', 2);
411
412    # Handle logical feature interactions.
413
81
110
    if ($use_feature{hardening}{relro} == 0) {
414        # Disable bindnow if relro is not enabled, since it has no
415        # hardening ability without relro and may incur load penalties.
416
0
0
        $use_feature{hardening}{bindnow} = 0;
417    }
418
81
118
    if ($use_feature{hardening}{stackprotector} == 0) {
419        # Disable stackprotectorstrong if stackprotector is disabled.
420
0
0
        $use_feature{hardening}{stackprotectorstrong} = 0;
421    }
422
423    ## Commit
424
425    # Set used features to their builtin setting if unset.
426
81
166
    foreach my $area (sort keys %builtin_feature) {
427
162
405
112
558
        while (my ($feature, $enabled) = each %{$builtin_feature{$area}}) {
428
243
268
            $flags->set_builtin($area, $feature, $enabled);
429        }
430    }
431
432    # Store the feature usage.
433
81
188
    foreach my $area (sort keys %use_feature) {
434
567
2430
339
3400
        while (my ($feature, $enabled) = each %{$use_feature{$area}}) {
435
1863
1543
            $flags->set_feature($area, $feature, $enabled);
436        }
437    }
438}
439
440sub add_build_flags {
441
81
1
107
    my ($self, $flags) = @_;
442
443    ## Global default flags
444
445
81
214
    my @compile_flags = qw(
446        CFLAGS
447        CXXFLAGS
448        OBJCFLAGS
449        OBJCXXFLAGS
450        FFLAGS
451        FCFLAGS
452    );
453
454
81
89
    my $default_flags;
455    my $default_d_flags;
456
457
81
120
    my $optimize_level = $flags->get_option_value('optimize-level');
458
81
109
    $default_flags = "-g -O$optimize_level";
459
81
103
    if ($optimize_level == 0) {
460
0
0
        $default_d_flags = '-fdebug';
461    } else {
462
81
75
        $default_d_flags = '-frelease';
463    }
464
465
81
219
    $flags->append($_, $default_flags) foreach @compile_flags;
466
81
231
    $flags->append('DFLAGS', $default_d_flags);
467
468    ## Area: abi
469
470
81
118
    my %abi_builtins = $flags->get_builtins('abi');
471
81
120
    my $cc_abi_time64 = $flags->get_option_value('cc-abi-time64');
472
473
81
163
    if ($flags->use_feature('abi', 'lfs') && ! $abi_builtins{lfs}) {
474
18
33
        $flags->append('CPPFLAGS',
475                       '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64');
476    } elsif (! $flags->use_feature('abi', 'lfs') &&
477             ! $abi_builtins{lfs} && $cc_abi_time64) {
478
3
3
        $flags->append('CPPFLAGS',
479                       '-U_LARGEFILE_SOURCE -U_FILE_OFFSET_BITS');
480    }
481
482
81
106
    if ($flags->use_feature('abi', 'time64') && ! $abi_builtins{time64}) {
483
9
17
        $flags->append('CPPFLAGS', '-D_TIME_BITS=64');
484    } elsif (! $flags->use_feature('abi', 'time64') &&
485             ! $abi_builtins{time64} && $cc_abi_time64) {
486
3
4
        $flags->append('CPPFLAGS', '-U_TIME_BITS');
487    }
488
489    ## Area: qa
490
491    # Warnings that detect actual bugs.
492
81
123
    if ($flags->use_feature('qa', 'bug-implicit-func')) {
493
75
101
        $flags->append('CFLAGS', '-Werror=implicit-function-declaration');
494    } else {
495
6
7
        $flags->append('CFLAGS', '-Wno-error=implicit-function-declaration');
496    }
497
81
108
    if ($flags->use_feature('qa', 'bug')) {
498        # C/C++ flags
499
6
7
        my @cfamilyflags = qw(
500            array-bounds
501            clobbered
502            volatile-register-var
503        );
504
6
6
        foreach my $warnflag (@cfamilyflags) {
505
18
22
            $flags->append('CFLAGS', "-Werror=$warnflag");
506
18
19
            $flags->append('CXXFLAGS', "-Werror=$warnflag");
507        }
508    }
509
510    # Inject dummy canary options to detect issues with build flag propagation.
511
81
120
    if ($flags->use_feature('qa', 'canary')) {
512
0
0
        require Digest::MD5;
513
0
0
        my $id = Digest::MD5::md5_hex(int rand 4096);
514
515
0
0
        foreach my $flag (qw(CPPFLAGS CFLAGS OBJCFLAGS CXXFLAGS OBJCXXFLAGS)) {
516
0
0
            $flags->append($flag, "-D__DEB_CANARY_${flag}_${id}__");
517        }
518
0
0
        $flags->append('LDFLAGS', "-Wl,-z,deb-canary-${id}");
519    }
520
521    ## Area: reproducible
522
523    # Warn when the __TIME__, __DATE__ and __TIMESTAMP__ macros are used.
524
81
144
    if ($flags->use_feature('reproducible', 'timeless')) {
525
81
106
       $flags->append('CPPFLAGS', '-Wdate-time');
526    }
527
528    # Avoid storing the build path in the binaries.
529
81
108
    if ($flags->use_feature('reproducible', 'fixfilepath') or
530        $flags->use_feature('reproducible', 'fixdebugpath')) {
531
81
99
        my $build_path = $flags->get_option_value('build-path');
532
81
72
        my $map;
533
534        # -ffile-prefix-map is a superset of -fdebug-prefix-map, prefer it
535        # if both are set.
536
81
89
        if ($flags->use_feature('reproducible', 'fixfilepath')) {
537
81
96
            $map = '-ffile-prefix-map=' . $build_path . '=.';
538        } else {
539
0
0
            $map = '-fdebug-prefix-map=' . $build_path . '=.';
540        }
541
542
81
136
        $flags->append($_, $map) foreach @compile_flags;
543    }
544
545    ## Area: optimize
546
547
81
107
    if ($flags->use_feature('optimize', 'lto')) {
548
9
8
        my $flag = '-flto=auto -ffat-lto-objects';
549
9
11
        $flags->append($_, $flag) foreach (@compile_flags, 'LDFLAGS');
550    }
551
552    ## Area: sanitize
553
554
81
100
    if ($flags->use_feature('sanitize', 'address')) {
555
0
0
        my $flag = '-fsanitize=address -fno-omit-frame-pointer';
556
0
0
        $flags->append('CFLAGS', $flag);
557
0
0
        $flags->append('CXXFLAGS', $flag);
558
0
0
        $flags->append('LDFLAGS', '-fsanitize=address');
559    }
560
561
81
101
    if ($flags->use_feature('sanitize', 'thread')) {
562
0
0
        my $flag = '-fsanitize=thread';
563
0
0
        $flags->append('CFLAGS', $flag);
564
0
0
        $flags->append('CXXFLAGS', $flag);
565
0
0
        $flags->append('LDFLAGS', $flag);
566    }
567
568
81
104
    if ($flags->use_feature('sanitize', 'leak')) {
569
0
0
        $flags->append('LDFLAGS', '-fsanitize=leak');
570    }
571
572
81
91
    if ($flags->use_feature('sanitize', 'undefined')) {
573
0
0
        my $flag = '-fsanitize=undefined';
574
0
0
        $flags->append('CFLAGS', $flag);
575
0
0
        $flags->append('CXXFLAGS', $flag);
576
0
0
        $flags->append('LDFLAGS', $flag);
577    }
578
579    ## Area: hardening
580
581    # PIE
582
81
125
    my $use_pie = $flags->get_feature('hardening', 'pie');
583
81
120
    my %hardening_builtins = $flags->get_builtins('hardening');
584
81
261
    if (defined $use_pie && $use_pie && ! $hardening_builtins{pie}) {
585
0
0
        my $flag = "-specs=$Dpkg::DATADIR/pie-compile.specs";
586
0
0
        $flags->append($_, $flag) foreach @compile_flags;
587
0
0
        $flags->append('LDFLAGS', "-specs=$Dpkg::DATADIR/pie-link.specs");
588    } elsif (defined $use_pie && ! $use_pie && $hardening_builtins{pie}) {
589
0
0
        my $flag = "-specs=$Dpkg::DATADIR/no-pie-compile.specs";
590
0
0
        $flags->append($_, $flag) foreach @compile_flags;
591
0
0
        $flags->append('LDFLAGS', "-specs=$Dpkg::DATADIR/no-pie-link.specs");
592    }
593
594    # Stack protector
595
81
98
    if ($flags->use_feature('hardening', 'stackprotectorstrong')) {
596
81
73
        my $flag = '-fstack-protector-strong';
597
81
124
        $flags->append($_, $flag) foreach @compile_flags;
598    } elsif ($flags->use_feature('hardening', 'stackprotector')) {
599
0
0
        my $flag = '-fstack-protector --param=ssp-buffer-size=4';
600
0
0
        $flags->append($_, $flag) foreach @compile_flags;
601    }
602
603    # Stack clash
604
81
124
    if ($flags->use_feature('hardening', 'stackclash')) {
605
42
36
        my $flag = '-fstack-clash-protection';
606
42
60
        $flags->append($_, $flag) foreach @compile_flags;
607    }
608
609    # Fortify Source
610
81
107
    if ($flags->use_feature('hardening', 'fortify')) {
611
81
94
        my $fortify_level = $flags->get_option_value('fortify-level');
612
81
134
        $flags->append('CPPFLAGS', "-D_FORTIFY_SOURCE=$fortify_level");
613    }
614
615    # Format Security
616
81
111
    if ($flags->use_feature('hardening', 'format')) {
617
81
72
        my $flag = '-Wformat -Werror=format-security';
618
81
99
        $flags->append('CFLAGS', $flag);
619
81
88
        $flags->append('CXXFLAGS', $flag);
620
81
91
        $flags->append('OBJCFLAGS', $flag);
621
81
92
        $flags->append('OBJCXXFLAGS', $flag);
622    }
623
624    # Read-only Relocations
625
81
97
    if ($flags->use_feature('hardening', 'relro')) {
626
81
88
        $flags->append('LDFLAGS', '-Wl,-z,relro');
627    }
628
629    # Bindnow
630
81
100
    if ($flags->use_feature('hardening', 'bindnow')) {
631
0
0
        $flags->append('LDFLAGS', '-Wl,-z,now');
632    }
633
634    # Branch protection
635
81
112
    if ($flags->use_feature('hardening', 'branch')) {
636
33
43
        my $cpu = $flags->get_option_value('hardening-branch-cpu');
637
33
24
        my $flag;
638
33
70
        if ($cpu eq 'arm64') {
639
0
0
            $flag = '-mbranch-protection=standard';
640        } elsif ($cpu eq 'amd64') {
641
33
33
            $flag = '-fcf-protection';
642        }
643        # The following should always be true on Debian, but it might not
644        # be on derivatives.
645
33
36
        if (defined $flag) {
646
33
43
            $flags->append($_, $flag) foreach @compile_flags;
647        }
648    }
649
650    # XXX: Handle *_FOR_BUILD flags here until we can properly initialize them.
651
81
264
    require Dpkg::Arch;
652
653
81
152
    my $host_arch = Dpkg::Arch::get_host_arch();
654
81
148
    my $build_arch = Dpkg::Arch::get_build_arch();
655
656
81
137
    if ($host_arch eq $build_arch) {
657
72
162
        foreach my $flag ($flags->list()) {
658
1440
1838
            next if $flag =~ m/_FOR_BUILD$/;
659
720
626
            my $value = $flags->get($flag);
660
720
756
            $flags->append($flag . '_FOR_BUILD', $value);
661        }
662    } else {
663
9
22
        $flags->append($_ . '_FOR_BUILD', $default_flags) foreach @compile_flags;
664
9
10
        $flags->append('DFLAGS_FOR_BUILD', $default_d_flags);
665    }
666}
667
668sub _build_tainted_by {
669
0
    my $self = shift;
670
0
    my %tainted;
671
672
0
    foreach my $pathname (qw(/bin /sbin /lib /lib32 /libo32 /libx32 /lib64)) {
673
0
        next unless -l $pathname;
674
675
0
        my $linkname = readlink $pathname;
676
0
        if ($linkname eq "usr$pathname" or $linkname eq "/usr$pathname") {
677
0
            $tainted{'merged-usr-via-aliased-dirs'} = 1;
678
0
            last;
679        }
680    }
681
682
0
    require File::Find;
683
0
    my %usr_local_types = (
684        configs => [ qw(etc) ],
685        includes => [ qw(include) ],
686        programs => [ qw(bin sbin) ],
687        libraries => [ qw(lib) ],
688    );
689
0
    foreach my $type (keys %usr_local_types) {
690        File::Find::find({
691
0
            wanted => sub { $tainted{"usr-local-has-$type"} = 1 if -f },
692            no_chdir => 1,
693
0
0
0
0
        }, grep { -d } map { "/usr/local/$_" } @{$usr_local_types{$type}});
694    }
695
696
0
    my @tainted = sort keys %tainted;
697
0
    return @tainted;
698}
699
700 - 706
=head1 CHANGES

=head2 Version 0.xx

This is a private module.

=cut
707
7081;