From patchwork Tue Nov 28 06:32:04 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tobin Harding X-Patchwork-Id: 10078737 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5D10C6056A for ; Tue, 28 Nov 2017 06:32:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2023C28E12 for ; Tue, 28 Nov 2017 06:32:28 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1506F29166; Tue, 28 Nov 2017 06:32:28 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id C2DBA28E12 for ; Tue, 28 Nov 2017 06:32:26 +0000 (UTC) Received: (qmail 19870 invoked by uid 550); 28 Nov 2017 06:32:25 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 19827 invoked from network); 28 Nov 2017 06:32:24 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tobin.cc; h=cc :date:from:message-id:subject:to:x-me-sender:x-me-sender :x-sasl-enc; s=fm1; bh=Ibtn247erW4zqtUc9guc+aBcRdq1X3YCjZ0ALNF9A Xk=; b=plnGzdO64MN8XY7GxhbnJUb/USlsEfgY6NsV9DzhBqg46OyAq/gOJzLSY dulbuzZLe/rYvWQrCn6sI1QyGkmxDkZOeUZ5ZX7bvwxYoKPGwF4MIuv9BqXbYCjV RshDjmOedzHGMHEwDc491/NrhuI3G05EI2OREvNLH3MPwpsK/itulUrRNGWDm/k/ X7mXtTU8PmOqtbqyVtT+l39N+xX2k5K5mDtiFNtgqkG0ejHX+1oXV60KZ/RPg/S4 zjkK+cJPYaCxuzsW1FWt8UJ03cURilT6hW/0/Z85YXl9cDZSMUY1afYu9y6lI0Aj gJEMi90WHlk907vG2K97cQ9INqjHQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:message-id:subject:to :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; bh=Ibtn247erW4zqtUc9 guc+aBcRdq1X3YCjZ0ALNF9AXk=; b=iHYBkHIkwAoFCHGH04jOB6Q9OoG3rfuh9 TMWWqEZqkHpAE7m6va8L1O9Dl+4zTA7zchiOsMeeksI1Sx9HkcBOd441GKNv8DCg uwKb78cxgfSYz0hD5RJfyU4VDepm5YS/SKElVAH04ff1ilKcroI0Z2Nk4jePGk0c 9Cfq0ERQjDJ7g/eizUrqcMwsMy2m3p5FyuNn91nW2qyPDlZ02IX8OHzuU+V6J5hP yZcAiUTzZPE/Mil7dENn3UhqfEIxQeS5VW74eJvGXSRP0bqZKIa4pAFRkI0sWyaa 05sP5BQwkOcAtPvifpQvxpoE4h7iW3si4OuF2GF55ByX7L0gYoP5Q== X-ME-Sender: From: "Tobin C. Harding" To: kaiwan.billimoria@gmail.com Cc: "Tobin C. Harding" , linux-kernel@vger.kernel.org, kernel-hardening@lists.openwall.com Date: Tue, 28 Nov 2017 17:32:04 +1100 Message-Id: <1511850724-2381-1-git-send-email-me@tobin.cc> X-Mailer: git-send-email 2.7.4 Subject: [kernel-hardening] [PATCH] leaking_addresses: add support for 32-bit kernel addresses X-Virus-Scanned: ClamAV using ClamSMTP Currently, leaking_addresses.pl only supports scanning 64 bit architectures. This is due to how the regular expressions are formed. We can do better than this. 32 architectures can be supported if we take into consideration the kernel virtual address split. Add support for ix86 32 bit architectures. - Add command line option for page offset. - Add command line option for kernel configuration file. - Parse kernel config file for page offset (CONFIG_PAGE_OFFSET). - Use page offset when checking for kernel virtual addresses. Signed-off-by: Kaiwan N Billimoria Signed-off-by: Tobin C. Harding --- As discussed this is a patch based on Kaiwan's previous patch. This patch represents co development by Kaiwan and Tobin. Applies on top of commit 4fbd8d194f06 (Linux 4.15-rc1) thanks, Tobin. scripts/leaking_addresses.pl | 168 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 20 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index bc5788000018..f03f2f140e0a 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -1,9 +1,11 @@ #!/usr/bin/env perl # # (c) 2017 Tobin C. Harding +# (c) 2017 Kaiwan N Billimoria (ix86 stuff) +# # 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). # @@ -22,6 +24,7 @@ use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); use Getopt::Long qw(:config no_auto_abbrev); use Config; +use feature 'state'; my $P = $0; my $V = '0.01'; @@ -35,18 +38,19 @@ 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; my $debug = 0; -my $raw = 0; -my $output_raw = ""; # Write raw results to file. -my $input_raw = ""; # Read raw results from file instead of scanning. - +my $raw = 0; # Show raw output. +my $output_raw = ""; # Write raw results to file. +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_file = ""; # Kernel configuration file. # Do not parse these files (absolute path). my @skip_parse_files_abs = ('/proc/kmsg', @@ -95,14 +99,16 @@ Version: $V Options: - -o, --output-raw= Save results for future processing. - -i, --input-raw= 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= Save results for future processing. + -i, --input-raw= 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= PAGE_OFFSET value (for 32-bit kernels). + --kernel-config-file= Kernel configuration file (e.g /boot/config) + -d, --debug Display debugging output. + -h, --help, --version Display this help and exit. Examples: @@ -115,7 +121,10 @@ Examples: # View summary report. $0 --input-raw scan.out --squash-by-filename -Scans the running (64 bit) kernel for potential leaking addresses. + # Scan kernel on a 32-bit system with a 2GB:2GB virtual address split. + $0 --page-offset-32bit=0x80000000 + +Scans the running kernel for potential leaking addresses. EOM exit($exitcode); @@ -131,6 +140,8 @@ GetOptions( 'squash-by-path' => \$squash_by_path, 'squash-by-filename' => \$squash_by_filename, 'raw' => \$raw, + 'page-offset-32bit=o' => \$page_offset_32bit, + 'kernel-config-file=s' => \$kernel_config_file, ) or help(1); help(0) if ($help); @@ -146,7 +157,9 @@ if (!$input_raw and ($squash_by_path or $squash_by_filename)) { exit(128); } -if (!is_supported_architecture()) { +if (is_supported_architecture()) { + show_detected_architecture() if $debug; +} else { printf "\nScript does not support your architecture, sorry.\n"; printf "\nCurrently we support: \n\n"; foreach(@SUPPORTED_ARCHITECTURES) { @@ -177,7 +190,7 @@ sub dprint 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 @@ -200,10 +213,40 @@ sub is_ppc64 return 0; } +sub is_ix86_32 +{ + my $archname = $Config{archname}; + + if ($archname =~ m/i[3456]86-linux/) { + return 1; + } + return 0; +} + +sub show_detected_architecture +{ + printf "Detected architecture: "; + if (is_ix86_32()) { + printf "32 bit x86\n"; + } elsif (is_x86_64()) { + printf "x86_64\n"; + } elsif (is_ppc64()) { + printf "ppc64\n"; + } else { + printf "failed to detect architecture\n" + } +} + sub is_false_positive { my ($match) = @_; + if (is_ix86_32()) { + return is_false_positive_ix86_32($match); + } + + # 64 bit architectures + if ($match =~ '\b(0x)?(f|F){16}\b' or $match =~ '\b(0x)?0{16}\b') { return 1; @@ -220,6 +263,87 @@ sub is_false_positive return 0; } +sub is_false_positive_ix86_32 +{ + my ($match) = @_; + state $page_offset = get_page_offset(); # only gets called once + + if ($match =~ '\b(0x)?(f|F){8}\b') { + return 1; + } + + my $addr32 = eval hex($match); + if ($addr32 < $page_offset) { + return 1; + } + + return 0; +} + +sub get_page_offset +{ + my $page_offset; + my $default_offset = "0xc0000000"; + my @config_files; + + # Allow --page-offset-32bit to over ride. + if ($page_offset_32bit != 0) { + return $page_offset_32bit; + } + + # Allow --kernel-config-file to over ride. + if ($kernel_config_file != "") { + @config_files = ($kernel_config_file); + } else { + my $config_file = '/boot/config-' . `uname -r`; + @config_files = ($config_file, '/boot/config'); + } + + if (-R "/proc/config.gz") { + my $tmp_file = "/tmp/tmpkconf"; + if (system("gunzip < /proc/config.gz > $tmp_file")) { + dprint " parse_kernel_config: system(gunzip...) failed\n"; + } else { + $page_offset = parse_kernel_config_file($tmp_file); + if ($page_offset ne "") { + return $page_offset; + } + } + system("rm -f $tmp_file"); + } + + foreach my $config_file (@config_files) { + $page_offset = parse_kernel_config($config_file); + if ($page_offset ne "") { + return $page_offset; + } + } + + printf STDERR "Failed to parse kernel config files\n"; + printf STDERR "Falling back to %s\n", $default_offset; + return $default_offset; +} + +sub parse_kernel_config_file +{ + my ($file) = @_; + my $config = 'CONFIG_PAGE_OFFSET'; + my $val = ""; + + open(my $fh, "<", $file) or return ""; + while (my $line = <$fh> ) { + if ($line =~ /^$config/) { + my ($str, $val) = split /=/, $line; + chomp($val); + last; + } + } + + close $fh; + return $val; +} + + # True if argument potentially contains a kernel address. sub may_leak_address { @@ -233,9 +357,11 @@ sub may_leak_address return 0; } - if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or - $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') { - return 0; + if (is_x86_64() or is_ppc64()) { + if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or + $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') { + return 0; + } } # One of these is guaranteed to be true. @@ -243,6 +369,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) {