| File: | Dpkg/Vendor/Debian.pm |
| Coverage: | 65.2% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 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 | |||||||
| 36 | package 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 | |||||||
| 50 | sub 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 | |||||||
| 113 | sub init_build_features { | ||||||
| 114 | 81 | 0 | 128 | my ($self, $use_feature, $builtin_feature) = @_; | |||
| 115 | } | ||||||
| 116 | |||||||
| 117 | sub 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 | |||||||
| 440 | sub 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 | |||||||
| 668 | sub _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 | |||||||
| 708 | 1; | ||||||