diff mbox

[1/5] UBUNTU: config-check -- add a configuration enforcer

Message ID 1260466909-28038-2-git-send-email-apw@canonical.com
State Superseded
Delegated to: Andy Whitcroft
Headers show

Commit Message

Andy Whitcroft Dec. 10, 2009, 5:41 p.m. UTC
Add a new configuration option value checker and enforcer.  This allow
us to specify the values we expect various configuration options to have
and to do that depending on the architecture and flavour of the build.
This is applied both as an early build check and at updateconfigs time.

Signed-off-by: Andy Whitcroft <apw@canonical.com>
---
 debian.master/config/enforce            |   25 +++++
 debian.master/rules.d/2-binary-arch.mk  |    2 +-
 debian.master/rules.d/4-checks.mk       |    8 ++
 debian.master/scripts/config-check      |  168 +++++++++++++++++++++++++++++++
 debian.master/scripts/misc/kernelconfig |   25 +++++-
 5 files changed, 226 insertions(+), 2 deletions(-)
 create mode 100644 debian.master/config/enforce
 create mode 100755 debian.master/scripts/config-check
diff mbox

Patch

diff --git a/debian.master/config/enforce b/debian.master/config/enforce
new file mode 100644
index 0000000..605ab65
--- /dev/null
+++ b/debian.master/config/enforce
@@ -0,0 +1,25 @@ 
+#
+# SECURITY items
+#
+# Ensure this option is enabled.
+value CONFIG_COMPAT_BRK n
+value CONFIG_DEVKMEM n
+value CONFIG_LSM_MMAP_MIN_ADDR 0
+value CONFIG_SECCOMP y
+value CONFIG_SECURITY y
+value CONFIG_SECURITY_FILE_CAPABILITIES y
+value CONFIG_SECURITY_SMACK y
+value CONFIG_SYN_COOKIES y
+# For architectures which support this option ensure it is enabled.
+!exists CONFIG_CC_STACKPROTECTOR | value CONFIG_CC_STACKPROTECTOR y
+!exists CONFIG_DEBUG_RODATA | value CONFIG_DEBUG_RODATA y
+!exists CONFIG_STRICT_DEVMEM | value CONFIG_STRICT_DEVMEM y
+# For architectures which support this option ensure it is disabled.
+!exists CONFIG_COMPAT_VDSO | value CONFIG_COMPAT_VDSO n
+# Default to 32768 for armel, 65536 for everything else.
+(( arch armel | arch sparc ) & value CONFIG_DEFAULT_MMAP_MIN_ADDR 32768 ) | \
+	( value CONFIG_DEFAULT_MMAP_MIN_ADDR 65536)
+
+# CONFIG_USB_DEVICE_FS breaks udev USB firmware loading and is deprecated
+# ensure it is disabled.
+value CONFIG_USB_DEVICEFS n
diff --git a/debian.master/rules.d/2-binary-arch.mk b/debian.master/rules.d/2-binary-arch.mk
index 4468d25..fb0d2a3 100644
--- a/debian.master/rules.d/2-binary-arch.mk
+++ b/debian.master/rules.d/2-binary-arch.mk
@@ -3,7 +3,7 @@ 
 
 # Prepare the out-of-tree build directory
 
-prepare-%: $(stampdir)/stamp-prepare-%
+prepare-%: $(stampdir)/stamp-prepare-% prepare-checks-%
 	@# Empty for make to be happy
 $(stampdir)/stamp-prepare-%: target_flavour = $*
 $(stampdir)/stamp-prepare-%: $(commonconfdir)/config.common.$(family) $(archconfdir)/config.common.$(arch) $(archconfdir)/config.flavour.%
diff --git a/debian.master/rules.d/4-checks.mk b/debian.master/rules.d/4-checks.mk
index 778984c..093639e 100644
--- a/debian.master/rules.d/4-checks.mk
+++ b/debian.master/rules.d/4-checks.mk
@@ -24,3 +24,11 @@  module-check-%: $(abidir)/%.modules
 
 checks-%: abi-check-% module-check-%
 	@# Will be calling more stuff later
+
+# Check the config against the known options list.
+config-prepare-check-%: $(stampdir)/stamp-prepare-%
+	@perl -f $(DEBIAN)/scripts/config-check \
+		$(builddir)/build-$*/.config "$(arch)" "$*" "$(commonconfdir)" "$(skipconfig)"
+
+prepare-checks-%: config-prepare-check-%
+	@# Will be calling more stuff later
diff --git a/debian.master/scripts/config-check b/debian.master/scripts/config-check
new file mode 100755
index 0000000..e43465c
--- /dev/null
+++ b/debian.master/scripts/config-check
@@ -0,0 +1,168 @@ 
+#!/usr/bin/perl
+#
+# check-config -- check the current config for issues
+#
+use strict;
+
+my $P = 'check-config';
+
+if ($#ARGV != 4) {
+	die "Usage: $P <config> <arch> <flavour> <commonconfig> <warn-only>\n";
+}
+
+my ($config, $arch, $flavour, $commonconfig, $warn_only) = @ARGV;
+
+my $checks = "$commonconfig/enforce";
+
+# If we are in overridden then still perform the checks and emit the messages
+# but do not return failure.  Those items marked FATAL will alway trigger
+# failure.
+my $fail_exit = 1;
+$fail_exit = 0 if ($warn_only eq 'true' || $warn_only eq '1');
+my $exit_val = 0;
+
+# Load up the current configuration values -- FATAL if this fails
+print "$P: $config: loading config\n";
+my %values = ();
+open(CONFIG, "<$config") || die "$P: $config: open failed -- $! -- aborting\n";
+while (<CONFIG>) {
+	# Pull out values.
+	/^#*\s*(CONFIG_\w+)[\s=](.*)$/ or next;
+	if ($2 eq 'is not set') {
+		$values{$1} = 'n';
+	} else {
+		$values{$1} = $2;
+	}
+}
+close(CONFIG);
+
+# Predicate execution engine.
+sub pred_first {
+	my ($rest) = @_;
+	my $depth = 0;
+	my $char;
+	my $pred;
+	
+	for ($char = 0; $char <= length($rest); $char++) {
+		if (substr($rest, $char, 1) eq '(') {
+			$depth++;
+		} elsif (substr($rest, $char, 1) eq ')') {
+			if (--$depth == 0) {
+				$char++;
+				last;
+			}
+		} elsif ($depth == 0 && substr($rest, $char, 1) eq '&') {
+			$char--;
+			last;
+		} elsif ($depth == 0 && substr($rest, $char, 1) eq '|') {
+			$char--;
+			last;
+		}
+	}
+	if ($depth != 0) {
+		die "$P: $rest: missing close parenthesis ')'\n";
+	}
+
+	($pred, $rest) = (substr($rest, 0, $char), substr($rest, $char + 1));
+
+	$pred =~ s/^\s*\(//;
+	$pred =~ s/\)\s*$//;
+	$pred =~ s/^\s*//;
+	$pred =~ s/\s*$//;
+
+	($pred, $rest);
+}
+		
+sub pred_do {
+	my ($pred) = @_;
+	my (@a) = split(' ', $pred);
+
+	if ($a[0] eq 'arch') {
+		die "$P: $pred: malformed -- $pred <arch>\n" if ($#a != 1);
+		#print "    *** ARCH<$arch ?? $a[1]>\n";
+		return ($arch eq $a[1])
+	} elsif ($a[0] eq 'flavour') {
+		die "$P: $pred: malformed -- $pred <flavour>\n" if ($#a != 1);
+		#print "    *** FLAVOUR<$flavour ?? $a[1]>\n";
+		return ($flavour eq $a[1])
+	} elsif ($a[0] eq 'value') {
+		die "$P: $pred: malformed -- $pred <name> <val>\n" if ($#a != 2);
+		#print "    *** CHECK<$a[1] $a[2] ?? " . $values{$a[1]} . ">\n";
+		return ($values{$a[1]} eq $a[2]);
+	} elsif ($a[0] eq 'exists') {
+		die "$P: $pred: malformed -- $pred <name>\n" if ($#a != 1);
+		return (defined $values{$a[1]});
+	} else {
+		die "$P: $pred: unknown predicate\n";
+	}
+	return 1;
+}
+sub pred_exec {
+	my ($rest) = @_;
+	my $pred;
+	my $res;
+	my $res2;
+	my $invert = 0;
+
+	#print "pred_exec('$rest')\n";
+
+	($pred, $rest) = pred_first($rest);
+	if ($pred =~ /^\s*!\s*(.*)$/) {
+		($pred, $invert) = ($1, 1);
+	}
+	if ($rest =~ /^\s*$/) {
+		$res = pred_do($pred);
+		$res = !$res if ($invert);
+
+	} elsif ($rest =~ /^\s*(\||\&)\s*(.*)$/) {
+		my ($op, $right) = ($1, $2);
+		
+		#print "  *left<$pred> op<$op> right<$right>\n";
+		$res = pred_exec($pred);
+		$res = !$res if ($invert);
+		if (($res && $op eq '&') || (!$res && $op eq '|')) {
+			#print "  left<$pred> op<$op> *right<$right>\n";
+			$res = pred_exec($right);
+		}
+
+	} else {
+		die "$P: $pred$rest: malformed predicate\n";
+	}
+	#print "res<$rest> -> pred<$pred> rest<$rest>\n";
+	return $res;
+}
+
+# FATAL: Check if we have an enforcement list.
+my $pass = 0;
+my $total = 0;
+my $line = '';
+print "$P: $checks: loading checks\n";
+open(CHECKS, "<$checks") || die "$P: $checks: open failed -- $! -- aborting\n";
+while (<CHECKS>) {
+	/^#/ && next;
+	chomp;
+
+	$line .= $_;
+	if ($line =~ /\\$/) {
+		chop($line);
+		$line .= " ";
+		next;
+	}
+	$line =~ /^\s*$/ && next;
+
+	#print "CHECK: <$line>\n";
+	$total++;
+	my $result = pred_exec($line);
+	if (!$result) {
+		print "$P: FAIL: $line\n";
+		$exit_val = $fail_exit;
+	} else {
+		$pass++;
+	}
+
+	$line = '';
+}
+close(CHECKS);
+
+print "$P: $pass/$total checks passed -- exit $exit_val\n";
+exit $exit_val;
diff --git a/debian.master/scripts/misc/kernelconfig b/debian.master/scripts/misc/kernelconfig
index 4bf7c1d..c01624e 100755
--- a/debian.master/scripts/misc/kernelconfig
+++ b/debian.master/scripts/misc/kernelconfig
@@ -116,7 +116,6 @@  for arch in $archs; do
 done
 
 rm -f $common_conf
-rm -rf build
 
 # Now run splitconfig.pl on all the config.common.<arch> copied to
 # $tmpdir
@@ -130,3 +129,27 @@  rm -rf build
 			>$arch/config.common.$arch
 	done
 )
+
+echo ""
+echo "Running config-check for all configurations ..."
+echo ""
+fail=0
+for arch in $archs; do
+	archconfdir=$confdir/$arch
+	flavourconfigs=$(cd $archconfdir && ls config.flavour.*)
+	for config in $flavourconfigs; do
+		if [ -f $archconfdir/$config ]; then
+			fullconf="$tmpdir/$arch-$config-full"
+			"$bindir/../config-check" "$fullconf" "$arch" "$config" "$confdir" "0" || let "fail=$fail+1"
+		fi
+	done
+done
+
+if [ "$fail" != 0 ]; then
+	echo ""
+	echo "*** ERROR: $fail config-check failures detected"
+	echo ""
+fi
+
+rm -rf build
+