From patchwork Wed Nov 8 03:37:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tobin Harding X-Patchwork-Id: 10047611 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 24C2E603FA for ; Wed, 8 Nov 2017 03:38:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 178EC2A3A9 for ; Wed, 8 Nov 2017 03:38:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0C2302A3AB; Wed, 8 Nov 2017 03:38:46 +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 CB9962A3A9 for ; Wed, 8 Nov 2017 03:38:44 +0000 (UTC) Received: (qmail 11508 invoked by uid 550); 8 Nov 2017 03:38:16 -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 11394 invoked from network); 8 Nov 2017 03:38:12 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tobin.cc; h=cc :date:from:in-reply-to:message-id:references:subject:to :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; bh=nSCNo456MQDs0kO9s CxSqZUJC7HKe2b3i+V4lZ9mr9o=; b=uVuXk0YPpOoIVZl5TGAEBMcGeU+2VcWK1 oY/4e4kGXgtbSxz7ZL1AxUItdM+gwseNqAqhCzzWvipG0HbJIDhtT7m4tEsgTyd9 q7ideBONUPk2s2lIkyvw0t2MkbJ63w4r/UqffTwP71Am3RwBXRZOhZ+mqrw75fTx ljGP9Dl2Dqe6JOGhT8NJ7Ma7zdMx55DHAYM+Aqc/3a3VB3pMuQhEfrltHnkNK3sr pVPuGQvN/2goiFYCTxBGdB5RO7FhIHWKQ37oPWltSmTAmhxgpHWm3ozmJ/cWmCzJ VfQhssh/IdBGTF2c7esPOMi0xq4AhEhdrorirpQEcGKNl18AEWY5Q== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; bh=nSCNo456MQDs0kO9sCxSqZUJC7HKe2b3i+V4lZ9mr9o=; b=RHbRNqBa 3ag7Bhd58SeqIoamlC+2MjrbhnfSfEUIBFjBXxV09MMDWk9AWMqboa5RiJB6jL8S 9rDLV9H1hkLFlqa0EllaVE8Ols2CVubt7E0PEBp94jR5AYs2zAuXoSLVf7nFXxGi b9wQ5EjgI9cxRgVjBvVEpz3vRORfJBCRpGeLW4kBRcf6swP3dkNfdbYlnaFlKFfo yUX7r9uPNypXsHROtLwBcUS2TaFwdhYOCSeX7L+kx2ZnPnXz9iZwqZbIFUbvN/X0 AmcMAJ0QDL+xq8XsyRSaIlCPQIVGZKrQPjtiKU+nB9PFOPssL1OeNZ8Wl3Ewbn0s pxYaZE+q0Ynrag== X-ME-Sender: From: "Tobin C. Harding" To: Linus Torvalds Cc: "Tobin C. Harding" , "Jason A. Donenfeld" , Theodore Ts'o , Kees Cook , Paolo Bonzini , Tycho Andersen , "Roberts, William C" , Tejun Heo , Jordan Glover , Greg KH , Petr Mladek , Joe Perches , Ian Campbell , Sergey Senozhatsky , Catalin Marinas , Will Deacon , Steven Rostedt , Chris Fries , Dave Weinstein , Daniel Micay , Djalal Harouni , linux-kernel@vger.kernel.org, Network Development , David Miller , kernel-hardening@lists.openwall.com, "Paul E. McKenney" , Andy Lutomirski , Peter Zijlstra Date: Wed, 8 Nov 2017 14:37:36 +1100 Message-Id: <1510112259-11572-5-git-send-email-me@tobin.cc> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1510112259-11572-1-git-send-email-me@tobin.cc> References: <1510112259-11572-1-git-send-email-me@tobin.cc> Subject: [kernel-hardening] [PATCH 4/7] scripts/leaking_addresses: add reporting X-Virus-Scanned: ClamAV using ClamSMTP Currently script just dumps all results found. Potentially, this risks loosing single results among multiple duplicate results. We need some way of restricting duplicates to assist users of the script. It would also be nice if we got a report instead of raw results. Duplicates can be defined in various ways, instead of trying to find a single perfect solution we can present the user with various options to display the output. Doing so will typically lead to users wanting to view the output multiple times. Currently we scan the kernel each time, this is slow and unnecessary. We can expedite the process by writing the results to file for subsequent viewing. Add sub-commands `scan` and `format`. Display output as a report instead of raw results. Add --raw flag to view raw results. Save results to file. For subsequent calls to `format` parse output file instead of re-scanning. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 201 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 187 insertions(+), 14 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 719ed0aaede7..4c31e935319b 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -21,14 +21,19 @@ use File::Spec; use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); use Getopt::Long qw(:config no_auto_abbrev); +use File::Spec::Functions 'catfile'; my $P = $0; my $V = '0.01'; -# Directories to scan. +# Directories to scan (we scan `dmesg` also). my @DIRS = ('/proc', '/sys'); # Command line options. +my $output = "scan.out"; +my $suppress_dmesg = 0; +my $squash_by_path = 0; +my $raw = 0; my $help = 0; my $debug = 0; @@ -70,21 +75,34 @@ sub help my ($exitcode) = @_; print << "EOM"; -Usage: $P [OPTIONS] + +Usage: $P COMMAND [OPTIONS] Version: $V +Commands: + + scan Scan the kernel (savesg raw results to file and runs `format`). + format Parse results file and format output. + Options: - -d, --debug Display debugging output. - -h, --help, --version Display this help and exit. + -o, --output= Raw results output file, used for later formatting. + --suppress-dmesg Do not show dmesg results. + --squash-by-path Show one result per unique path. + --raw Show raw results. + -d, --debug Display debugging output. + -h, --help, --version Display this help and exit. Scans the running (64 bit) kernel for potential leaking addresses. - EOM exit($exitcode); } GetOptions( + 'o|output=s' => \$output, + 'suppress-dmesg' => \$suppress_dmesg, + 'squash-by-path' => \$squash_by_path, + 'raw' => \$raw, 'd|debug' => \$debug, 'h|help' => \$help, 'version' => \$help @@ -92,8 +110,21 @@ GetOptions( help(0) if ($help); -parse_dmesg(); -walk(@DIRS); +my ($command) = @ARGV; +if (not defined $command) { + help(128); +} + +if ($command ne 'scan' and $command ne 'format') { + printf "\nUnknown command: %s\n\n", $command; + help(128); +} + +if ($command eq 'scan') { + scan(); +} + +format_output(); exit 0; @@ -102,6 +133,17 @@ sub dprint printf(STDERR @_) if $debug; } +sub scan +{ + open (my $fh, '>', "$output") or die "$0: $output: $!\n"; + select $fh; + + parse_dmesg(); + walk(@DIRS); + + select STDOUT; +} + sub is_false_positive { my ($match) = @_; @@ -120,30 +162,39 @@ sub is_false_positive return 0; } -# True if argument potentially contains a kernel address. sub may_leak_address { my ($line) = @_; + + my @addresses = extract_addresses($line); + return @addresses > 0; +} + +# Return _all_ non false positive addresses from $line. +sub extract_addresses +{ + my ($line) = @_; my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b'; + my (@addresses, @empty); # Signal masks. if ($line =~ '^SigBlk:' or $line =~ '^SigCgt:') { - return 0; + return @empty; } if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') { - return 0; + return @empty; } - while (/($address)/g) { + while ($line =~ /($address)/g) { if (!is_false_positive($1)) { - return 1; + push @addresses, $1; } } - return 0; + return @addresses; } sub parse_dmesg @@ -203,7 +254,6 @@ sub parse_file close $fh; } - # True if we should skip walking this directory. sub skip_walk { @@ -236,3 +286,126 @@ sub walk } } } + +sub format_output +{ + if ($raw) { + dump_raw_output(); + return; + } + + my ($total, $dmesg, $paths, $files) = parse_raw_file(); + + printf "\nTotal number of results from scan (incl dmesg): %d\n", $total; + + if (!$suppress_dmesg) { + print_dmesg($dmesg); + } + squash_by($files, 'filename'); + + if ($squash_by_path) { + squash_by($paths, 'path'); + } +} + +sub dump_raw_output +{ + open (my $fh, '<', $output) or die "$0: $output: $!\n"; + while (<$fh>) { + print $_; + } + close $fh; +} + +sub print_dmesg +{ + my ($dmesg) = @_; + + print "\ndmesg output:\n"; + + if (@$dmesg == 0) { + print "\n"; + return; + } + + foreach(@$dmesg) { + my $index = index($_, ': '); + $index += 2; # skid ': ' + print substr($_, $index); + } +} + +sub squash_by +{ + my ($ref, $desc) = @_; + + print "\nResults squashed by $desc (excl dmesg). "; + print "Displaying [ <$desc>], \n"; + + if (keys %$ref == 0) { + print "\n"; + return; + } + + foreach(keys %$ref) { + my $lines = $ref->{$_}; + my $length = @$lines; + printf "[%d %s] %s", $length, $_, @$lines[0]; + } +} + +sub parse_raw_file +{ + my $total = 0; # Total number of lines parsed. + my @dmesg; # dmesg output. + my %files; # Unique filenames containing leaks. + my %paths; # Unique paths containing leaks. + + open (my $fh, '<', $output) or die "$0: $output: $!\n"; + while (my $line = <$fh>) { + $total++; + + if ("dmesg:" eq substr($line, 0, 6)) { + push @dmesg, $line; + next; + } + + cache_path(\%paths, $line); + cache_filename(\%files, $line); + } + + return $total, \@dmesg, \%paths, \%files; +} + +sub cache_path +{ + my ($paths, $line) = @_; + + my $index = index($line, ': '); + my $path = substr($line, 0, $index); + + $index += 2; # skip ': ' + add_to_cache($paths, $path, substr($line, $index)); +} + +sub cache_filename +{ + my ($files, $line) = @_; + + my $index = index($line, ': '); + my $path = substr($line, 0, $index); + my $filename = basename($path); + + $index += 2; # skip ': ' + add_to_cache($files, $filename, substr($line, $index)); +} + +sub add_to_cache +{ + my ($cache, $key, $value) = @_; + + if (!$cache->{$key}) { + $cache->{$key} = (); + } + push @{$cache->{$key}}, $value; +}