From 1d208333d42fc1606542d27346c16ad1b71b3de4 Mon Sep 17 00:00:00 2001
From: Kees Cook <kees@outflux.net>
Date: Wed, 28 Dec 2011 15:03:44 -0800
Subject: [PATCH 2/3] BuildFlags: Create feature interface

Refactor the hardened compiler flag logic to use a new "get/set/has
features" interface to BuildFlags.pm so as to communicate the current
logical state of the hardening feature.

Signed-off-by: Kees Cook <kees@debian.org>
---
 scripts/Dpkg/BuildFlags.pm    |   40 ++++++++++++++++++++++++-
 scripts/Dpkg/Vendor/Debian.pm |   66 ++++++++++++++++++++++++++++------------
 2 files changed, 85 insertions(+), 21 deletions(-)

diff --git a/scripts/Dpkg/BuildFlags.pm b/scripts/Dpkg/BuildFlags.pm
index 6112a9f..e832b39 100644
--- a/scripts/Dpkg/BuildFlags.pm
+++ b/scripts/Dpkg/BuildFlags.pm
@@ -18,7 +18,7 @@ package Dpkg::BuildFlags;
 use strict;
 use warnings;
 
-our $VERSION = "1.01";
+our $VERSION = "1.02";
 
 use Dpkg::Gettext;
 use Dpkg::BuildOptions;
@@ -68,6 +68,7 @@ sub load_vendor_defaults {
     my ($self) = @_;
     $self->{'options'} = {};
     $self->{'source'} = {};
+    $self->{'features'} = {};
     my $build_opts = Dpkg::BuildOptions->new();
     my $default_flags = $build_opts->has("noopt") ? "-g -O0" : "-g -O2";
     $self->{flags} = {
@@ -202,6 +203,19 @@ sub set {
     $self->{origin}->{$flag} = $src if defined $src;
 }
 
+=item $bf->set_feature($area, $feature, $enabled)
+
+Update the boolean state of whether a specific feature within a known
+feature area has been enabled. The only currently known feature area is
+"hardening".
+
+=cut
+
+sub set_feature {
+    my ($self, $area, $feature, $enabled) = @_;
+    $self->{features}->{$area}->{$feature} = !!$enabled;
+}
+
 =item $bf->strip($flag, $value, $source)
 
 Update the build flag $flag by stripping the flags listed in $value and
@@ -306,6 +320,18 @@ sub get {
     return $self->{'flags'}{$key};
 }
 
+=item $bf->get_features($area)
+
+Return, for the given area, a hash with keys as feature names, and values
+as booleans indicating whether the feature is enabled or not.
+
+=cut
+
+sub get_features {
+    my ($self, $area) = @_;
+    return %{$self->{'features'}{$area}};
+}
+
 =item $bf->get_origin($flag)
 
 Return the origin associated to the flag. It might be undef if the
@@ -318,6 +344,18 @@ sub get_origin {
     return $self->{'origin'}{$key};
 }
 
+=item $bf->has_features($area)
+
+Returns true if the given area of features is known, and false otherwise.
+The only currently recognized area is "hardening".
+
+=cut
+
+sub has_features {
+    my ($self, $area) = @_;
+    return exists $self->{'features'}{$area};
+}
+
 =item $bf->has($option)
 
 Returns a boolean indicating whether the flags exists in the object.
diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm
index e824d0e..07935d9 100644
--- a/scripts/Dpkg/Vendor/Debian.pm
+++ b/scripts/Dpkg/Vendor/Debian.pm
@@ -83,7 +83,7 @@ sub add_hardening_flags {
     my $arch = get_host_arch();
     my ($abi, $os, $cpu) = debarch_to_debtriplet($arch);
 
-    # Decide what's enabled
+    # Features enabled by default for all builds.
     my %use_feature = (
 	"pie" => 0,
 	"stackprotector" => 1,
@@ -92,6 +92,8 @@ sub add_hardening_flags {
 	"relro" => 1,
 	"bindnow" => 0
     );
+
+    # Adjust features based on Maintainer's desires.
     my $opts = Dpkg::BuildOptions->new(envvar => "DEB_BUILD_MAINT_OPTIONS");
     foreach my $feature (split(",", $opts->get("hardening") // "")) {
 	$feature = lc($feature);
@@ -112,47 +114,71 @@ sub add_hardening_flags {
 	}
     }
 
-    # PIE
-    if ($use_feature{"pie"} and
-	$os =~ /^(linux|knetbsd|hurd)$/ and
-	$cpu !~ /^(hppa|m68k|mips|mipsel|avr32)$/) {
-	# Only on linux/knetbsd/hurd (see #430455 and #586215)
+    # Mask features that are not available on certain architectures.
+    if ($os !~ /^(linux|knetbsd|hurd)$/ or
+	$cpu =~ /^(hppa|m68k|mips|mipsel|avr32)$/) {
+	# Disabled on non-linux/knetbsd/hurd (see #430455 and #586215).
 	# Disabled on hppa, m68k (#451192), mips/mipsel (#532821), avr32
-	# (#574716)
-	$flags->append("CFLAGS", "-fPIE");
-	$flags->append("CXXFLAGS", "-fPIE");
-	$flags->append("LDFLAGS", "-fPIE -pie");
+	#  (#574716).
+	$use_feature{"pie"} = 0;
     }
-    # Stack protector
-    if ($use_feature{"stackprotector"} and
-	$cpu !~ /^(ia64|alpha|mips|mipsel|hppa)$/ and $arch ne "arm") {
+    if ($cpu =~ /^(ia64|alpha|mips|mipsel|hppa)$/ or $arch eq "arm") {
 	# Stack protector disabled on ia64, alpha, mips, mipsel, hppa.
 	#   "warning: -fstack-protector not supported for this target"
 	# Stack protector disabled on arm (ok on armel).
 	#   compiler supports it incorrectly (leads to SEGV)
+	$use_feature{"stackprotector"} = 0;
+    }
+    if ($cpu =~ /^(ia64|hppa|avr32)$/) {
+	# relro not implemented on ia64, hppa, avr32.
+	$use_feature{"relro"} = 0;
+    }
+
+    # Handle logical feature interactions.
+    if ($use_feature{"relro"} == 0) {
+	# Disable bindnow if relro is not enabled, since it has no
+	# hardening ability without relro and may incur load penalties.
+	$use_feature{"bindnow"} = 0;
+    }
+
+    # PIE
+    if ($use_feature{"pie"}) {
+	$flags->append("CFLAGS", "-fPIE");
+	$flags->append("CXXFLAGS", "-fPIE");
+	$flags->append("LDFLAGS", "-fPIE -pie");
+    }
+
+    # Stack protector
+    if ($use_feature{"stackprotector"}) {
 	$flags->append("CFLAGS", "-fstack-protector --param=ssp-buffer-size=4");
 	$flags->append("CXXFLAGS", "-fstack-protector --param=ssp-buffer-size=4");
     }
-    # Fortify
+
+    # Fortify Source
     if ($use_feature{"fortify"}) {
 	$flags->append("CPPFLAGS", "-D_FORTIFY_SOURCE=2");
     }
-    # Format
+
+    # Format Security
     if ($use_feature{"format"}) {
 	$flags->append("CFLAGS", "-Wformat -Wformat-security -Werror=format-security");
 	$flags->append("CXXFLAGS", "-Wformat -Wformat-security -Werror=format-security");
     }
-    # Relro
-    if ($use_feature{"relro"} and $cpu !~ /^(ia64|hppa|avr32)$/) {
+
+    # Read-only Relocations
+    if ($use_feature{"relro"}) {
 	$flags->append("LDFLAGS", "-Wl,-z,relro");
-    } else {
-	# Disable full relro if relro is not enabled.
-	$use_feature{"bindnow"} = 0;
     }
+
     # Bindnow
     if ($use_feature{"bindnow"}) {
 	$flags->append("LDFLAGS", "-Wl,-z,now");
     }
+
+    # Store the feature usage.
+    for (keys %use_feature) {
+	$flags->set_feature("hardening", $_, $use_feature{$_})
+    }
 }
 
 1;
-- 
1.7.5.4

