All of lore.kernel.org
 help / color / mirror / Atom feed
From: kaiwan.billimoria@gmail.com
To: "Tobin C. Harding" <me@tobin.cc>
Cc: linux-kernel@vger.kernel.org,
	"kernel-hardening@lists.openwall.com"
	<kernel-hardening@lists.openwall.com>
Subject: [kernel-hardening] [PATCH v2] scripts: leaking_addresses: add support for 32-bit kernel addresses
Date: Mon, 27 Nov 2017 08:42:16 +0530	[thread overview]
Message-ID: <1511752336.31585.2.camel@gmail.com> (raw)

Currently, leaking_addresses.pl only supports scanning and displaying 'leaked'
64-bit kernel virtual addresses. We can scan for and display 'leaked' 32-bit
kernel virtual addresses as well.

Briefly, the way it works: once it detects we're running on an i'x'86 platform,
(where x=3|4|5|6), it takes this arch into account for checking. The essential
rationale:
 if 32-bit-virt-addr >= PAGE_OFFSET => it's a kernel virtual address.

This version programatically queries and sets PAGE_OFFSET based on it's value
in one of these files: /boot/config, /boot/config-$(uname -r) and
/proc/config.gz. If, for any reason, none of these files can be used, we
fallback to requesting the user to pass PAGE_OFFSET as an option switch.

Feedback welcome..


Kaiwan N Billimoria (1):
  scripts: leaking_addresses: add support for 32-bit kernel addresses

 scripts/leaking_addresses.pl | 150 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 132 insertions(+), 18 deletions(-)

Signed-off-by: Kaiwan N Billimoria <kaiwan.billimoria@gmail.com>
---
diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl
index 2d5336b3e1ea..fccd0a5094f1 100755
--- a/scripts/leaking_addresses.pl
+++ b/scripts/leaking_addresses.pl
@@ -5,7 +5,7 @@
 
 # Licensed under the terms of the GNU GPL License version 2
 #
-# leaking_addresses.pl: Scan 64 bit kernel for potential leaking addresses.
+# leaking_addresses.pl: Scan the kernel for potential leaking addresses.
 #  - Scans dmesg output.
 #  - Walks directory tree and parses each file (for each directory in @DIRS).
 #
@@ -14,7 +14,7 @@
 #
 # You may like to set kptr_restrict=2 before running script
 # (see Documentation/sysctl/kernel.txt).
-
+#
 use warnings;
 use strict;
 use POSIX;
@@ -37,7 +37,7 @@ my $TIMEOUT = 10;
 # Script can only grep for kernel addresses on the following architectures. If
 # your architecture is not listed here and has a grep'able kernel address please
 # consider submitting a patch.
-my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64');
+my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64', 'i[3456]86');
 
 # Command line options.
 my $help = 0;
@@ -49,6 +49,9 @@ my $input_raw = "";	# Read raw results from file instead of scanning.
 my $suppress_dmesg = 0;		# Don't show dmesg in output.
 my $squash_by_path = 0;		# Summary report grouped by absolute path.
 my $squash_by_filename = 0;	# Summary report grouped by filename.
+my $page_offset_32bit = 0;      # 32-bit: value of CONFIG_PAGE_OFFSET
+
+my @kernel_config_files = ('/boot/config', '/boot/config-'.`uname -r`, '/proc/config.gz');
 
 # Do not parse these files (absolute path).
 my @skip_parse_files_abs = ('/proc/kmsg',
@@ -97,14 +100,15 @@ Version: $V
 
 Options:
 
-	-o, --output-raw=<file>  Save results for future processing.
-	-i, --input-raw=<file>   Read results from file instead of scanning.
-	    --raw                Show raw results (default).
-	    --suppress-dmesg     Do not show dmesg results.
-	    --squash-by-path     Show one result per unique path.
-	    --squash-by-filename Show one result per unique filename.
-	-d, --debug              Display debugging output.
-	-h, --help, --version    Display this help and exit.
+	-o, --output-raw=<file>    Save results for future processing.
+	-i, --input-raw=<file>     Read results from file instead of scanning.
+	      --raw                    Show raw results (default).
+	      --suppress-dmesg         Do not show dmesg results.
+	      --squash-by-path         Show one result per unique path.
+	      --squash-by-filename     Show one result per unique filename.
+	--page-offset-32bit=<hex>  PAGE_OFFSET value (for 32-bit kernels).
+	-d, --debug                Display debugging output.
+	-h, --help, --version      Display this help and exit.
 
 Examples:
 
@@ -117,7 +121,11 @@ Examples:
 	# View summary report.
 	$0 --input-raw scan.out --squash-by-filename
 
-Scans the running (64 bit) kernel for potential leaking addresses.
+	# (On a 32-bit system with a 2GB:2GB VMSPLIT), pass PAGE_OFFSET value
+	# as an option switch.
+	$0 --page-offset-32bit=0x80000000
+
+Scans the running kernel for potential leaking addresses.
 
 EOM
 	exit($exitcode);
@@ -133,10 +141,16 @@ GetOptions(
 	'squash-by-path'        => \$squash_by_path,
 	'squash-by-filename'    => \$squash_by_filename,
 	'raw'                   => \$raw,
+	'page-offset-32bit=o'   => \$page_offset_32bit,
 ) or help(1);
 
 help(0) if ($help);
 
+sub dprint
+{
+	printf(STDERR @_) if $debug;
+}
+
 if ($input_raw) {
 	format_output($input_raw);
 	exit(0);
@@ -162,6 +176,20 @@ if (!is_supported_architecture()) {
 	exit(129);
 }
 
+if ($debug) {
+	printf "Detected arch : ";
+	if (is_ix86_32()) {
+		printf "32 bit x86\n";
+	} else {
+		printf "64 bit\n";
+	}
+}
+
+if (is_ix86_32()) {
+	$page_offset_32bit = get_page_offset();
+	dprint "PAGE_OFFSET = 0x%X\n", $page_offset_32bit;
+}
+
 if ($output_raw) {
 	open my $fh, '>', $output_raw or die "$0: $output_raw: $!\n";
 	select $fh;
@@ -172,14 +200,9 @@ walk(@DIRS);
 
 exit 0;
 
-sub dprint
-{
-	printf(STDERR @_) if $debug;
-}
-
 sub is_supported_architecture
 {
-	return (is_x86_64() or is_ppc64());
+	return (is_x86_64() or is_ppc64() or is_ix86_32());
 }
 
 sub is_x86_64
@@ -202,6 +225,17 @@ sub is_ppc64
 	return 0;
 }
 
+# 32-bit x86: is_i'x'86_32() ; where is [3 or 4 or 5 or 6]
+sub is_ix86_32
+{
+	my $archname = $Config{archname};
+
+	if ($archname =~ m/i[3456]86-linux/) {
+		return 1;
+	}
+	return 0;
+}
+
 sub is_false_positive
 {
 	my ($match) = @_;
@@ -217,6 +251,14 @@ sub is_false_positive
 		    $match =~ '\bf{10}601000\b') {
 			return 1;
 		}
+	} elsif (is_ix86_32()) {
+		my $addr32 = eval hex($match);
+		if ($addr32 < $page_offset_32bit ) {
+			return 1;
+		}
+		if ($match =~ '\b(0x)?(f|F){8}\b') {
+			return 1;
+		}
 	}
 
 	return 0;
@@ -245,6 +287,8 @@ sub may_leak_address
 		$address_re = '\b(0x)?ffff[[:xdigit:]]{12}\b';
 	} elsif (is_ppc64()) {
 		$address_re = '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b';
+	} elsif (is_ix86_32()) {
+		$address_re = '\b(0x)?[[:xdigit:]]{8}\b';
 	}
 
 	while (/($address_re)/g) {
@@ -501,3 +545,73 @@ sub add_to_cache
 	}
 	push @{$cache->{$key}}, $value;
 }
+
+sub parse_kernel_config
+{
+	my ($file, $config) = @_;
+	my $str;
+	my $val = NULL;
+	my $gzipfile = 0;
+
+	# Explicitly check for '/proc/config.gz'
+	if ($file eq "/proc/config.gz") {
+		$gzipfile = 1;
+		if (! -R $file) {
+			dprint "parse_kernel_config: /proc/config.gz does not exist\n";
+			return NULL;
+		}
+		if (system("gunzip < /proc/config.gz > /tmp/tmpkconf")) {
+			dprint " parse_kernel_config: system(gunzip...) failed\n";
+			return NULL;
+		}
+		$file = "/tmp/tmpkconf";
+		$file =~ s/\R*//g;
+	}
+
+	dprint "32-bit: attempting to parse file \"$file\" for config \"$config\" ...\n";
+	if (! -R $file) {
+		dprint " parse_kernel_config: file does not exist or not readable\n";
+		return NULL;
+	}
+
+	open my $fh, "<", $file or return;
+	while (my $line = <$fh> ) {
+		if ($line =~ /^$config/) {
+			($str,$val) = split /=/, $line;
+		}
+	}
+	close $fh;
+	if ($gzipfile == 1) {
+		system("rm -f /tmp/tmpkconf");
+	}
+
+	if ($val eq NULL) {
+		return NULL;
+	}
+	$val =~ s/\R*//g;
+	return $val;
+}
+
+sub get_page_offset
+{
+	my $page_offset = $page_offset_32bit;
+
+	# If option --page-offset-32bit has been passed, just use it, else
+	# parse PAGE_OFFSET by iterating over an array of kernel config files.
+	if ($page_offset == 0) {
+		foreach my $kconfig_file (@kernel_config_files) {
+			$kconfig_file =~ s/\R*//g;
+			$page_offset = eval parse_kernel_config($kconfig_file, "CONFIG_PAGE_OFFSET");
+			if ($page_offset != 0) {
+				last;
+			}
+		}
+		if ($page_offset == 0) {
+			printf STDERR "$P: Fatal Error :: couldn't parse CONFIG_PAGE_OFFSET, aborting...\n";
+			printf STDERR "You can pass it via the option switch --page-offset-32bit=<value>\n";
+			exit(1);
+		}
+	}
+	return $page_offset;
+}
+
-- 
2.14.3

WARNING: multiple messages have this Message-ID (diff)
From: kaiwan.billimoria@gmail.com
To: "Tobin C. Harding" <me@tobin.cc>
Cc: linux-kernel@vger.kernel.org,
	"kernel-hardening@lists.openwall.com" 
	<kernel-hardening@lists.openwall.com>
Subject: [PATCH v2] scripts: leaking_addresses: add support for 32-bit kernel addresses
Date: Mon, 27 Nov 2017 08:42:16 +0530	[thread overview]
Message-ID: <1511752336.31585.2.camel@gmail.com> (raw)

Currently, leaking_addresses.pl only supports scanning and displaying 'leaked'
64-bit kernel virtual addresses. We can scan for and display 'leaked' 32-bit
kernel virtual addresses as well.

Briefly, the way it works: once it detects we're running on an i'x'86 platform,
(where x=3|4|5|6), it takes this arch into account for checking. The essential
rationale:
 if 32-bit-virt-addr >= PAGE_OFFSET => it's a kernel virtual address.

This version programatically queries and sets PAGE_OFFSET based on it's value
in one of these files: /boot/config, /boot/config-$(uname -r) and
/proc/config.gz. If, for any reason, none of these files can be used, we
fallback to requesting the user to pass PAGE_OFFSET as an option switch.

Feedback welcome..


Kaiwan N Billimoria (1):
  scripts: leaking_addresses: add support for 32-bit kernel addresses

 scripts/leaking_addresses.pl | 150 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 132 insertions(+), 18 deletions(-)

Signed-off-by: Kaiwan N Billimoria <kaiwan.billimoria@gmail.com>
---
diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl
index 2d5336b3e1ea..fccd0a5094f1 100755
--- a/scripts/leaking_addresses.pl
+++ b/scripts/leaking_addresses.pl
@@ -5,7 +5,7 @@
 
 # Licensed under the terms of the GNU GPL License version 2
 #
-# leaking_addresses.pl: Scan 64 bit kernel for potential leaking addresses.
+# leaking_addresses.pl: Scan the kernel for potential leaking addresses.
 #  - Scans dmesg output.
 #  - Walks directory tree and parses each file (for each directory in @DIRS).
 #
@@ -14,7 +14,7 @@
 #
 # You may like to set kptr_restrict=2 before running script
 # (see Documentation/sysctl/kernel.txt).
-
+#
 use warnings;
 use strict;
 use POSIX;
@@ -37,7 +37,7 @@ my $TIMEOUT = 10;
 # Script can only grep for kernel addresses on the following architectures. If
 # your architecture is not listed here and has a grep'able kernel address please
 # consider submitting a patch.
-my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64');
+my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64', 'i[3456]86');
 
 # Command line options.
 my $help = 0;
@@ -49,6 +49,9 @@ my $input_raw = "";	# Read raw results from file instead of scanning.
 my $suppress_dmesg = 0;		# Don't show dmesg in output.
 my $squash_by_path = 0;		# Summary report grouped by absolute path.
 my $squash_by_filename = 0;	# Summary report grouped by filename.
+my $page_offset_32bit = 0;      # 32-bit: value of CONFIG_PAGE_OFFSET
+
+my @kernel_config_files = ('/boot/config', '/boot/config-'.`uname -r`, '/proc/config.gz');
 
 # Do not parse these files (absolute path).
 my @skip_parse_files_abs = ('/proc/kmsg',
@@ -97,14 +100,15 @@ Version: $V
 
 Options:
 
-	-o, --output-raw=<file>  Save results for future processing.
-	-i, --input-raw=<file>   Read results from file instead of scanning.
-	    --raw                Show raw results (default).
-	    --suppress-dmesg     Do not show dmesg results.
-	    --squash-by-path     Show one result per unique path.
-	    --squash-by-filename Show one result per unique filename.
-	-d, --debug              Display debugging output.
-	-h, --help, --version    Display this help and exit.
+	-o, --output-raw=<file>    Save results for future processing.
+	-i, --input-raw=<file>     Read results from file instead of scanning.
+	      --raw                    Show raw results (default).
+	      --suppress-dmesg         Do not show dmesg results.
+	      --squash-by-path         Show one result per unique path.
+	      --squash-by-filename     Show one result per unique filename.
+	--page-offset-32bit=<hex>  PAGE_OFFSET value (for 32-bit kernels).
+	-d, --debug                Display debugging output.
+	-h, --help, --version      Display this help and exit.
 
 Examples:
 
@@ -117,7 +121,11 @@ Examples:
 	# View summary report.
 	$0 --input-raw scan.out --squash-by-filename
 
-Scans the running (64 bit) kernel for potential leaking addresses.
+	# (On a 32-bit system with a 2GB:2GB VMSPLIT), pass PAGE_OFFSET value
+	# as an option switch.
+	$0 --page-offset-32bit=0x80000000
+
+Scans the running kernel for potential leaking addresses.
 
 EOM
 	exit($exitcode);
@@ -133,10 +141,16 @@ GetOptions(
 	'squash-by-path'        => \$squash_by_path,
 	'squash-by-filename'    => \$squash_by_filename,
 	'raw'                   => \$raw,
+	'page-offset-32bit=o'   => \$page_offset_32bit,
 ) or help(1);
 
 help(0) if ($help);
 
+sub dprint
+{
+	printf(STDERR @_) if $debug;
+}
+
 if ($input_raw) {
 	format_output($input_raw);
 	exit(0);
@@ -162,6 +176,20 @@ if (!is_supported_architecture()) {
 	exit(129);
 }
 
+if ($debug) {
+	printf "Detected arch : ";
+	if (is_ix86_32()) {
+		printf "32 bit x86\n";
+	} else {
+		printf "64 bit\n";
+	}
+}
+
+if (is_ix86_32()) {
+	$page_offset_32bit = get_page_offset();
+	dprint "PAGE_OFFSET = 0x%X\n", $page_offset_32bit;
+}
+
 if ($output_raw) {
 	open my $fh, '>', $output_raw or die "$0: $output_raw: $!\n";
 	select $fh;
@@ -172,14 +200,9 @@ walk(@DIRS);
 
 exit 0;
 
-sub dprint
-{
-	printf(STDERR @_) if $debug;
-}
-
 sub is_supported_architecture
 {
-	return (is_x86_64() or is_ppc64());
+	return (is_x86_64() or is_ppc64() or is_ix86_32());
 }
 
 sub is_x86_64
@@ -202,6 +225,17 @@ sub is_ppc64
 	return 0;
 }
 
+# 32-bit x86: is_i'x'86_32() ; where is [3 or 4 or 5 or 6]
+sub is_ix86_32
+{
+	my $archname = $Config{archname};
+
+	if ($archname =~ m/i[3456]86-linux/) {
+		return 1;
+	}
+	return 0;
+}
+
 sub is_false_positive
 {
 	my ($match) = @_;
@@ -217,6 +251,14 @@ sub is_false_positive
 		    $match =~ '\bf{10}601000\b') {
 			return 1;
 		}
+	} elsif (is_ix86_32()) {
+		my $addr32 = eval hex($match);
+		if ($addr32 < $page_offset_32bit ) {
+			return 1;
+		}
+		if ($match =~ '\b(0x)?(f|F){8}\b') {
+			return 1;
+		}
 	}
 
 	return 0;
@@ -245,6 +287,8 @@ sub may_leak_address
 		$address_re = '\b(0x)?ffff[[:xdigit:]]{12}\b';
 	} elsif (is_ppc64()) {
 		$address_re = '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b';
+	} elsif (is_ix86_32()) {
+		$address_re = '\b(0x)?[[:xdigit:]]{8}\b';
 	}
 
 	while (/($address_re)/g) {
@@ -501,3 +545,73 @@ sub add_to_cache
 	}
 	push @{$cache->{$key}}, $value;
 }
+
+sub parse_kernel_config
+{
+	my ($file, $config) = @_;
+	my $str;
+	my $val = NULL;
+	my $gzipfile = 0;
+
+	# Explicitly check for '/proc/config.gz'
+	if ($file eq "/proc/config.gz") {
+		$gzipfile = 1;
+		if (! -R $file) {
+			dprint "parse_kernel_config: /proc/config.gz does not exist\n";
+			return NULL;
+		}
+		if (system("gunzip < /proc/config.gz > /tmp/tmpkconf")) {
+			dprint " parse_kernel_config: system(gunzip...) failed\n";
+			return NULL;
+		}
+		$file = "/tmp/tmpkconf";
+		$file =~ s/\R*//g;
+	}
+
+	dprint "32-bit: attempting to parse file \"$file\" for config \"$config\" ...\n";
+	if (! -R $file) {
+		dprint " parse_kernel_config: file does not exist or not readable\n";
+		return NULL;
+	}
+
+	open my $fh, "<", $file or return;
+	while (my $line = <$fh> ) {
+		if ($line =~ /^$config/) {
+			($str,$val) = split /=/, $line;
+		}
+	}
+	close $fh;
+	if ($gzipfile == 1) {
+		system("rm -f /tmp/tmpkconf");
+	}
+
+	if ($val eq NULL) {
+		return NULL;
+	}
+	$val =~ s/\R*//g;
+	return $val;
+}
+
+sub get_page_offset
+{
+	my $page_offset = $page_offset_32bit;
+
+	# If option --page-offset-32bit has been passed, just use it, else
+	# parse PAGE_OFFSET by iterating over an array of kernel config files.
+	if ($page_offset == 0) {
+		foreach my $kconfig_file (@kernel_config_files) {
+			$kconfig_file =~ s/\R*//g;
+			$page_offset = eval parse_kernel_config($kconfig_file, "CONFIG_PAGE_OFFSET");
+			if ($page_offset != 0) {
+				last;
+			}
+		}
+		if ($page_offset == 0) {
+			printf STDERR "$P: Fatal Error :: couldn't parse CONFIG_PAGE_OFFSET, aborting...\n";
+			printf STDERR "You can pass it via the option switch --page-offset-32bit=<value>\n";
+			exit(1);
+		}
+	}
+	return $page_offset;
+}
+
-- 
2.14.3

             reply	other threads:[~2017-11-27  3:12 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-11-27  3:12 kaiwan.billimoria [this message]
2017-11-27  3:12 ` [PATCH v2] scripts: leaking_addresses: add support for 32-bit kernel addresses kaiwan.billimoria
2017-11-28  6:28 ` [kernel-hardening] " Tobin C. Harding
2017-11-28  6:28   ` Tobin C. Harding
2017-11-28 11:42   ` [kernel-hardening] " Kaiwan N Billimoria
2017-11-28 11:42     ` Kaiwan N Billimoria

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1511752336.31585.2.camel@gmail.com \
    --to=kaiwan.billimoria@gmail.com \
    --cc=kernel-hardening@lists.openwall.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=me@tobin.cc \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.