From 6d23dd9bbb2e71f1bc1bfbe77831e15cc505a995 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 22 Nov 2017 10:14:45 +1100 Subject: leaking_addresses: fix typo function not called Currently code uses a check against an undefined variable because the variable is a sub routine name and is not evaluated. Evaluate subroutine; add parenthesis to sub routine name. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index bc5788000018..b0efa21239ac 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -209,7 +209,7 @@ sub is_false_positive return 1; } - if (is_x86_64) { + if (is_x86_64()) { # vsyscall memory region, we should probably check against a range here. if ($match =~ '\bf{10}600000\b' or $match =~ '\bf{10}601000\b') { -- cgit v1.2.3-59-g8ed1b From 20cdfb5fc49550e66a972ed448e2dacb229aa5d1 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 7 Dec 2017 12:10:44 +1100 Subject: leaking_addresses: remove mention of kptr_restrict leaking_addresses.pl can be run with kptr_restrict==0 now, we don't need the comment about setting kptr_restrict any more. Remove comment suggesting setting kptr_restrict. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index b0efa21239ac..b10fd606cef6 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -9,9 +9,6 @@ # # Use --debug to output path before parsing, this is useful to find files that # cause the script to choke. -# -# You may like to set kptr_restrict=2 before running script -# (see Documentation/sysctl/kernel.txt). use warnings; use strict; -- cgit v1.2.3-59-g8ed1b From 6145de836a5ead2732667ac76917f5c296599f5f Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 7 Dec 2017 13:54:48 +1100 Subject: leaking_addresses: remove command examples Currently help output includes command examples. These were cute when we first started development of this script but are unnecessary. Remove command examples. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index b10fd606cef6..02393dbca583 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -101,17 +101,6 @@ Options: -d, --debug Display debugging output. -h, --help, --version Display this help and exit. -Examples: - - # Scan kernel and dump raw results. - $0 - - # Scan kernel and save results to file. - $0 --output-raw scan.out - - # View summary report. - $0 --input-raw scan.out --squash-by-filename - Scans the running (64 bit) kernel for potential leaking addresses. EOM -- cgit v1.2.3-59-g8ed1b From 15d60a35b8fe82363325494a2a7c49f26f9f5594 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 7 Dec 2017 13:57:53 +1100 Subject: leaking_addresses: indent dependant options A number of the command line options to script are dependant on the option --input-raw being set. If we indent these options it makes explicit this dependency. Indent options dependant on --input-raw. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 02393dbca583..31cf54ad379f 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -92,14 +92,14 @@ 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. + -d, --debug Display debugging output. + -h, --help, --version Display this help and exit. Scans the running (64 bit) kernel for potential leaking addresses. -- cgit v1.2.3-59-g8ed1b From 87e37588563da905a8506b8922cfba1d71382a64 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 7 Dec 2017 12:33:21 +1100 Subject: leaking_addresses: add range check for vsyscall memory Currently script checks only first and last address in the vsyscall memory range. We can do better than this. When checking for false positives against $match, we can convert $match to a hexadecimal value then check if it lies within the range of vsyscall addresses. Check whole range of vsyscall addresses when checking for false positive. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 31cf54ad379f..398e534f0e16 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -19,6 +19,7 @@ use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); use Getopt::Long qw(:config no_auto_abbrev); use Config; +use bigint qw/hex/; my $P = $0; my $V = '0.01'; @@ -195,17 +196,24 @@ sub is_false_positive return 1; } - if (is_x86_64()) { - # vsyscall memory region, we should probably check against a range here. - if ($match =~ '\bf{10}600000\b' or - $match =~ '\bf{10}601000\b') { - return 1; - } + if (is_x86_64() and is_in_vsyscall_memory_region($match)) { + return 1; } return 0; } +sub is_in_vsyscall_memory_region +{ + my ($match) = @_; + + my $hex = hex($match); + my $region_min = hex("0xffffffffff600000"); + my $region_max = hex("0xffffffffff601000"); + + return ($hex >= $region_min and $hex <= $region_max); +} + # True if argument potentially contains a kernel address. sub may_leak_address { -- cgit v1.2.3-59-g8ed1b From f9d2a42dacf96eb8a10259edafec0f66c9921d52 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 7 Dec 2017 13:53:41 +1100 Subject: leaking_addresses: add support for kernel config file Features that rely on the ability to get kernel configuration options are ready to be implemented in script. In preparation for this we can add support for kernel config options as a separate patch to ease review. Add support for locating and parsing kernel configuration file. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 66 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 398e534f0e16..b3ffbf8022ce 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -41,10 +41,10 @@ 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 $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 $kernel_config_file = ""; # Kernel configuration file. # Do not parse these files (absolute path). my @skip_parse_files_abs = ('/proc/kmsg', @@ -99,6 +99,7 @@ Options: --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. + --kernel-config-file= Kernel configuration file (e.g /boot/config) -d, --debug Display debugging output. -h, --help, --version Display this help and exit. @@ -118,6 +119,7 @@ GetOptions( 'squash-by-path' => \$squash_by_path, 'squash-by-filename' => \$squash_by_filename, 'raw' => \$raw, + 'kernel-config-file=s' => \$kernel_config_file, ) or help(1); help(0) if ($help); @@ -187,6 +189,68 @@ sub is_ppc64 return 0; } +# Gets config option value from kernel config file. +# Returns "" on error or if config option not found. +sub get_kernel_config_option +{ + my ($option) = @_; + my $value = ""; + my $tmp_file = ""; + my @config_files; + + # Allow --kernel-config-file to override. + if ($kernel_config_file ne "") { + @config_files = ($kernel_config_file); + } elsif (-R "/proc/config.gz") { + my $tmp_file = "/tmp/tmpkconf"; + + if (system("gunzip < /proc/config.gz > $tmp_file")) { + dprint "$0: system(gunzip < /proc/config.gz) failed\n"; + return ""; + } else { + @config_files = ($tmp_file); + } + } else { + my $file = '/boot/config-' . `uname -r`; + chomp $file; + @config_files = ($file, '/boot/config'); + } + + foreach my $file (@config_files) { + dprint("parsing config file: %s\n", $file); + $value = option_from_file($option, $file); + if ($value ne "") { + last; + } + } + + if ($tmp_file ne "") { + system("rm -f $tmp_file"); + } + + return $value; +} + +# Parses $file and returns kernel configuration option value. +sub option_from_file +{ + my ($option, $file) = @_; + my $str = ""; + my $val = ""; + + open(my $fh, "<", $file) or return ""; + while (my $line = <$fh> ) { + if ($line =~ /^$option/) { + ($str, $val) = split /=/, $line; + chomp $val; + last; + } + } + + close $fh; + return $val; +} + sub is_false_positive { my ($match) = @_; -- cgit v1.2.3-59-g8ed1b From 2f042c93a138f87a2f85e80daa5dbab6bf138045 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 7 Dec 2017 14:40:29 +1100 Subject: leaking_addresses: add support for 5 page table levels Currently script only supports 4 page table levels because of the way the kernel address regular expression is crafted. We can do better than this. Using previously added support for kernel configuration options we can get the number of page table levels defined by CONFIG_PGTABLE_LEVELS. Using this value a correct regular expression can be crafted. This only supports 5 page tables on x86_64. Add support for 5 page table levels on x86_64. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index b3ffbf8022ce..35d6dd9fdced 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -20,6 +20,7 @@ use Term::ANSIColor qw(:constants); use Getopt::Long qw(:config no_auto_abbrev); use Config; use bigint qw/hex/; +use feature 'state'; my $P = $0; my $V = '0.01'; @@ -296,13 +297,7 @@ sub may_leak_address return 0; } - # One of these is guaranteed to be true. - if (is_x86_64()) { - $address_re = '\b(0x)?ffff[[:xdigit:]]{12}\b'; - } elsif (is_ppc64()) { - $address_re = '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b'; - } - + $address_re = get_address_re(); while (/($address_re)/g) { if (!is_false_positive($1)) { return 1; @@ -312,6 +307,29 @@ sub may_leak_address return 0; } +sub get_address_re +{ + if (is_x86_64()) { + return get_x86_64_re(); + } elsif (is_ppc64()) { + return '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b'; + } +} + +sub get_x86_64_re +{ + # We handle page table levels but only if explicitly configured using + # CONFIG_PGTABLE_LEVELS. If config file parsing fails or config option + # is not found we default to using address regular expression suitable + # for 4 page table levels. + state $ptl = get_kernel_config_option('CONFIG_PGTABLE_LEVELS'); + + if ($ptl == 5) { + return '\b(0x)?ff[[:xdigit:]]{14}\b'; + } + return '\b(0x)?ffff[[:xdigit:]]{12}\b'; +} + sub parse_dmesg { open my $cmd, '-|', 'dmesg'; -- cgit v1.2.3-59-g8ed1b From 6efb7458280a8f5d4c1955324e9d8985e90a882b Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Sat, 6 Jan 2018 09:24:49 +1100 Subject: leaking_addresses: use system command to get arch Currently script uses Perl to get the machine architecture. This can be erroneous since Perl uses the architecture of the machine that Perl was compiled on not the architecture of the running machine. We should use the systems `uname` command instead. Use `uname -m` instead of Perl to get the machine architecture. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 35d6dd9fdced..56894daf6368 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -142,10 +142,10 @@ if (!is_supported_architecture()) { foreach(@SUPPORTED_ARCHITECTURES) { printf "\t%s\n", $_; } + printf("\n"); - my $archname = $Config{archname}; - printf "\n\$ perl -MConfig -e \'print \"\$Config{archname}\\n\"\'\n"; - printf "%s\n", $archname; + my $archname = `uname -m`; + printf("Machine hardware name (`uname -m`): %s\n", $archname); exit(129); } @@ -172,7 +172,7 @@ sub is_supported_architecture sub is_x86_64 { - my $archname = $Config{archname}; + my $archname = `uname -m`; if ($archname =~ m/x86_64/) { return 1; @@ -182,9 +182,9 @@ sub is_x86_64 sub is_ppc64 { - my $archname = $Config{archname}; + my $archname = `uname -m`; - if ($archname =~ m/powerpc/ and $archname =~ m/64/) { + if ($archname =~ m/ppc64/) { return 1; } return 0; -- cgit v1.2.3-59-g8ed1b From 5eb0da0568a241f72732eb2143538fb14879a52c Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 29 Jan 2018 14:33:49 +1100 Subject: leaking_addresses: add is_arch() wrapper subroutine Currently there is duplicate code when checking the architecture type. We can remove the duplication by implementing a wrapper function is_arch(). Implement and use wrapper function is_arch(). Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 56894daf6368..e5b418cca185 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -170,24 +170,26 @@ sub is_supported_architecture return (is_x86_64() or is_ppc64()); } -sub is_x86_64 +sub is_arch { - my $archname = `uname -m`; + my ($desc) = @_; + my $arch = `uname -m`; + + chomp $arch; + if ($arch eq $desc) { + return 1; + } + return 0; +} - if ($archname =~ m/x86_64/) { - return 1; - } - return 0; +sub is_x86_64 +{ + return is_arch('x86_64'); } sub is_ppc64 { - my $archname = `uname -m`; - - if ($archname =~ m/ppc64/) { - return 1; - } - return 0; + return is_arch('ppc64'); } # Gets config option value from kernel config file. -- cgit v1.2.3-59-g8ed1b From 1410fe4eea22959bd31c05e4c1846f1718300bde Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 29 Jan 2018 15:00:16 +1100 Subject: leaking_addresses: add 32-bit support Currently script only supports x86_64 and ppc64. It would be nice to be able to scan 32-bit machines also. We can add support for 32-bit architectures by modifying how we check for false positives, taking advantage of the page offset used by the kernel, and using the correct regular expression. Support for 32-bit machines is enabled by the observation that the kernel addresses on 32-bit machines are larger [in value] than the page offset. We can use this to filter false positives when scanning the kernel for leaking addresses. Programmatic determination of the running architecture is not immediately obvious (current 32-bit machines return various strings from `uname -m`). We therefore provide a flag to enable scanning of 32-bit kernels. Also we can check the kernel config file for the offset and if not found default to 0xc0000000. A command line option to parse in the page offset is also provided. We do automatically detect architecture if running on ix86. Add support for 32-bit kernels. Add a command line option for page offset. Suggested-by: Kaiwan N Billimoria Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 93 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 11 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index e5b418cca185..05906f6cf6b9 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -3,7 +3,7 @@ # (c) 2017 Tobin C. Harding # 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). # @@ -31,10 +31,9 @@ my @DIRS = ('/proc', '/sys'); # Timer for parsing each file, in seconds. 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'); +# Kernel addresses vary by architecture. We can only auto-detect the following +# architectures (using `uname -m`). (flag --32-bit overrides auto-detection.) +my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64', 'x86'); # Command line options. my $help = 0; @@ -46,6 +45,8 @@ 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 $kernel_config_file = ""; # Kernel configuration file. +my $opt_32bit = 0; # Scan 32-bit kernel. +my $page_offset_32bit = 0; # Page offset for 32-bit kernel. # Do not parse these files (absolute path). my @skip_parse_files_abs = ('/proc/kmsg', @@ -101,10 +102,12 @@ Options: --squash-by-path Show one result per unique path. --squash-by-filename Show one result per unique filename. --kernel-config-file= Kernel configuration file (e.g /boot/config) + --32-bit Scan 32-bit kernel. + --page-offset-32-bit=o Page offset (for 32-bit kernel 0xABCD1234). -d, --debug Display debugging output. -h, --help, --version Display this help and exit. -Scans the running (64 bit) kernel for potential leaking addresses. +Scans the running kernel for potential leaking addresses. EOM exit($exitcode); @@ -121,6 +124,8 @@ GetOptions( 'squash-by-filename' => \$squash_by_filename, 'raw' => \$raw, 'kernel-config-file=s' => \$kernel_config_file, + '32-bit' => \$opt_32bit, + 'page-offset-32-bit=o' => \$page_offset_32bit, ) or help(1); help(0) if ($help); @@ -136,7 +141,7 @@ if (!$input_raw and ($squash_by_path or $squash_by_filename)) { exit(128); } -if (!is_supported_architecture()) { +if (!(is_supported_architecture() or $opt_32bit or $page_offset_32bit)) { printf "\nScript does not support your architecture, sorry.\n"; printf "\nCurrently we support: \n\n"; foreach(@SUPPORTED_ARCHITECTURES) { @@ -144,6 +149,9 @@ if (!is_supported_architecture()) { } printf("\n"); + printf("If you are running a 32-bit architecture you may use:\n"); + printf("\n\t--32-bit or --page-offset-32-bit=\n\n"); + my $archname = `uname -m`; printf("Machine hardware name (`uname -m`): %s\n", $archname); @@ -167,7 +175,28 @@ 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_32bit +{ + # Allow --32-bit or --page-offset-32-bit to override + if ($opt_32bit or $page_offset_32bit) { + return 1; + } + + return is_ix86_32(); +} + +sub is_ix86_32 +{ + my $arch = `uname -m`; + + chomp $arch; + if ($arch =~ m/i[3456]86/) { + return 1; + } + return 0; } sub is_arch @@ -258,6 +287,12 @@ sub is_false_positive { my ($match) = @_; + if (is_32bit()) { + return is_false_positive_32bit($match); + } + + # 64 bit false positives. + if ($match =~ '\b(0x)?(f|F){16}\b' or $match =~ '\b(0x)?0{16}\b') { return 1; @@ -270,6 +305,40 @@ sub is_false_positive return 0; } +sub is_false_positive_32bit +{ + my ($match) = @_; + state $page_offset = get_page_offset(); + + if ($match =~ '\b(0x)?(f|F){8}\b') { + return 1; + } + + if (hex($match) < $page_offset) { + return 1; + } + + return 0; +} + +# returns integer value +sub get_page_offset +{ + my $page_offset; + my $default_offset = 0xc0000000; + + # Allow --page-offset-32bit to override. + if ($page_offset_32bit != 0) { + return $page_offset_32bit; + } + + $page_offset = get_kernel_config_option('CONFIG_PAGE_OFFSET'); + if (!$page_offset) { + return $default_offset; + } + return $page_offset; +} + sub is_in_vsyscall_memory_region { my ($match) = @_; @@ -311,11 +380,13 @@ sub may_leak_address sub get_address_re { - if (is_x86_64()) { - return get_x86_64_re(); - } elsif (is_ppc64()) { + if (is_ppc64()) { return '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b'; + } elsif (is_32bit()) { + return '\b(0x)?[[:xdigit:]]{8}\b'; } + + return get_x86_64_re(); } sub get_x86_64_re -- cgit v1.2.3-59-g8ed1b From e2858caddc71f61521254a5359d17d058d6dda08 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 19 Feb 2018 10:22:15 +1100 Subject: leaking_addresses: do not parse binary files Currently script parses binary files. Since we are scanning for readable kernel addresses there is no need to parse binary files. We can use Perl to check if file is binary and skip parsing it if so. Do not parse binary files. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 05906f6cf6b9..3d5c3096aac8 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -462,6 +462,10 @@ sub parse_file return; } + if (! -T $file) { + return; + } + if (skip_parse($file)) { dprint "skipping file: $file\n"; return; -- cgit v1.2.3-59-g8ed1b From b401f56f33bf551304cc4ca4f503863ee6ac7787 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 19 Feb 2018 11:03:37 +1100 Subject: leaking_addresses: simplify path skipping Currently script has multiple configuration arrays. This is confusing, evident by the fact that a bunch of the entries are in the wrong place. We can simplify the code by just having a single array for absolute paths to skip and a single array for file names to skip wherever they appear in the scanned directory tree. There are also currently multiple subroutines to handle the different arrays, we can reduce these to a single subroutine also. Simplify the path skipping code. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 90 ++++++++++++++------------------------------ 1 file changed, 29 insertions(+), 61 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 3d5c3096aac8..2ad6e7fb6698 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -48,41 +48,26 @@ my $kernel_config_file = ""; # Kernel configuration file. my $opt_32bit = 0; # Scan 32-bit kernel. my $page_offset_32bit = 0; # Page offset for 32-bit kernel. -# Do not parse these files (absolute path). -my @skip_parse_files_abs = ('/proc/kmsg', - '/proc/kcore', - '/proc/fs/ext4/sdb1/mb_groups', - '/proc/1/fd/3', - '/sys/firmware/devicetree', - '/proc/device-tree', - '/sys/kernel/debug/tracing/trace_pipe', - '/sys/kernel/security/apparmor/revision'); - -# Do not parse these files under any subdirectory. -my @skip_parse_files_any = ('0', - '1', - '2', - 'pagemap', - 'events', - 'access', - 'registers', - 'snapshot_raw', - 'trace_pipe_raw', - 'ptmx', - 'trace_pipe'); - -# Do not walk these directories (absolute path). -my @skip_walk_dirs_abs = (); - -# Do not walk these directories under any subdirectory. -my @skip_walk_dirs_any = ('self', - 'thread-self', - 'cwd', - 'fd', - 'usbmon', - 'stderr', - 'stdin', - 'stdout'); +# Skip these absolute paths. +my @skip_abs = ( + '/proc/kmsg', + '/proc/device-tree', + '/sys/firmware/devicetree', + '/sys/kernel/debug/tracing/trace_pipe', + '/sys/kernel/security/apparmor/revision'); + +# Skip these under any subdirectory. +my @skip_any = ( + 'pagemap', + 'events', + 'access', + 'registers', + 'snapshot_raw', + 'trace_pipe_raw', + 'ptmx', + 'trace_pipe', + 'fd', + 'usbmon'); sub help { @@ -417,26 +402,20 @@ sub parse_dmesg # True if we should skip this path. sub skip { - my ($path, $paths_abs, $paths_any) = @_; + my ($path) = @_; - foreach (@$paths_abs) { + foreach (@skip_abs) { return 1 if (/^$path$/); } my($filename, $dirs, $suffix) = fileparse($path); - foreach (@$paths_any) { + foreach (@skip_any) { return 1 if (/^$filename$/); } return 0; } -sub skip_parse -{ - my ($path) = @_; - return skip($path, \@skip_parse_files_abs, \@skip_parse_files_any); -} - sub timed_parse_file { my ($file) = @_; @@ -466,12 +445,6 @@ sub parse_file return; } - if (skip_parse($file)) { - dprint "skipping file: $file\n"; - return; - } - dprint "parsing: $file\n"; - open my $fh, "<", $file or return; while ( <$fh> ) { if (may_leak_address($_)) { @@ -481,21 +454,12 @@ sub parse_file close $fh; } - -# True if we should skip walking this directory. -sub skip_walk -{ - my ($path) = @_; - return skip($path, \@skip_walk_dirs_abs, \@skip_walk_dirs_any) -} - # Recursively walk directory tree. sub walk { my @dirs = @_; while (my $pwd = shift @dirs) { - next if (skip_walk($pwd)); next if (!opendir(DIR, $pwd)); my @files = readdir(DIR); closedir(DIR); @@ -506,11 +470,15 @@ sub walk my $path = "$pwd/$file"; next if (-l $path); + next if (skip($path)); + if (-d $path) { push @dirs, $path; - } else { - timed_parse_file($path); + next; } + + dprint "parsing: $path\n"; + timed_parse_file($path); } } } -- cgit v1.2.3-59-g8ed1b From 5e4bac34edc7829b4a0749e3870d4a171c1f036f Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 19 Feb 2018 13:23:44 +1100 Subject: leaking_addresses: cache architecture name Currently we are repeatedly calling `uname -m`. This is causing the script to take a long time to run (more than 10 seconds to parse /proc/kallsyms). We can use Perl state variables to cache the result of the first call to `uname -m`. With this change in place the script scans the whole kernel in under a minute. Cache machine architecture in state variable. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 2ad6e7fb6698..6e5bc57caeaa 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -175,7 +175,7 @@ sub is_32bit sub is_ix86_32 { - my $arch = `uname -m`; + state $arch = `uname -m`; chomp $arch; if ($arch =~ m/i[3456]86/) { @@ -198,12 +198,14 @@ sub is_arch sub is_x86_64 { - return is_arch('x86_64'); + state $is = is_arch('x86_64'); + return $is; } sub is_ppc64 { - return is_arch('ppc64'); + state $is = is_arch('ppc64'); + return $is; } # Gets config option value from kernel config file. -- cgit v1.2.3-59-g8ed1b From 472c9e1085f20de71fc482500c8f1e4e45dff651 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 27 Feb 2018 15:02:57 +1100 Subject: leaking_addresses: skip all /proc/PID except /proc/1 When the system is idle it is likely that most files under /proc/PID will be identical for various processes. Scanning _all_ the PIDs under /proc is unnecessary and implies that we are thoroughly scanning /proc. This is _not_ the case because there may be ways userspace can trigger creation of /proc files that leak addresses but were not present during a scan. For these two reasons we should exclude all PID directories under /proc except '1/' Exclude all /proc/PID except /proc/1. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 6e5bc57caeaa..2075d98278f2 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -10,6 +10,14 @@ # Use --debug to output path before parsing, this is useful to find files that # cause the script to choke. +# +# When the system is idle it is likely that most files under /proc/PID will be +# identical for various processes. Scanning _all_ the PIDs under /proc is +# unnecessary and implies that we are thoroughly scanning /proc. This is _not_ +# the case because there may be ways userspace can trigger creation of /proc +# files that leak addresses but were not present during a scan. For these two +# reasons we exclude all PID directories under /proc except '1/' + use warnings; use strict; use POSIX; @@ -472,6 +480,10 @@ sub walk my $path = "$pwd/$file"; next if (-l $path); + # skip /proc/PID except /proc/1 + next if (($path =~ /^\/proc\/[0-9]+$/) && + ($path !~ /^\/proc\/1$/)); + next if (skip($path)); if (-d $path) { -- cgit v1.2.3-59-g8ed1b From 2ad742939283ed0613be654ad0aaf29b797f9905 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 27 Feb 2018 14:14:24 +1100 Subject: leaking_addresses: skip '/proc/1/syscall' The pointers listed in /proc/1/syscall are user pointers, and negative syscall args will show up like kernel addresses. For example /proc/31808/syscall: 0 0x3 0x55b107a38180 0x2000 0xffffffffffffffb0 \ 0x55b107a302d0 0x55b107a38180 0x7fffa313b8e8 0x7ff098560d11 Skip parsing /proc/1/syscall Suggested-by: Tycho Andersen Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 2075d98278f2..db6f39df879f 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -60,6 +60,7 @@ my $page_offset_32bit = 0; # Page offset for 32-bit kernel. my @skip_abs = ( '/proc/kmsg', '/proc/device-tree', + '/proc/1/syscall', '/sys/firmware/devicetree', '/sys/kernel/debug/tracing/trace_pipe', '/sys/kernel/security/apparmor/revision'); -- cgit v1.2.3-59-g8ed1b From 34827374492580b27c3cba29d493dab28c8c25d3 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 27 Feb 2018 15:15:34 +1100 Subject: leaking_addresses: remove version number We have git now, we don't need a version number. This was originally added because leaking_addresses.pl shamelessly (and mindlessly) copied checkpatch.pl Remove version number from script. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index db6f39df879f..ce3b9d5a5bbc 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -31,7 +31,6 @@ use bigint qw/hex/; use feature 'state'; my $P = $0; -my $V = '0.01'; # Directories to scan. my @DIRS = ('/proc', '/sys'); @@ -85,7 +84,6 @@ sub help print << "EOM"; Usage: $P [OPTIONS] -Version: $V Options: -- cgit v1.2.3-59-g8ed1b From 2306a67745ebdf3f98bc954248b74a3f1d57cdc2 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 2 Mar 2018 08:42:59 +1100 Subject: leaking_addresses: explicitly name variable used in regex Currently sub routine may_leak_address() is checking regex against Perl special variable $_ which is _fortunately_ being set correctly in a loop before this sub routine is called. We already have declared a variable to hold this value '$line' we should use it. Use $line in regex match instead of implicit $_ Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index ce3b9d5a5bbc..ba5f9709bced 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -363,7 +363,7 @@ sub may_leak_address } $address_re = get_address_re(); - while (/($address_re)/g) { + while ($line =~ /($address_re)/g) { if (!is_false_positive($1)) { return 1; } -- cgit v1.2.3-59-g8ed1b From c73dff595f259736a90f52b38cf5798abeae4a3c Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 2 Mar 2018 08:49:55 +1100 Subject: leaking_addresses: check if file name contains address Sometimes files may be created by using output from printk. As the scan traverses the directory tree we should parse each path name and check if it is leaking an address. Add check for leaking address on each path name. Suggested-by: Tycho Andersen Acked-by: Tycho Andersen Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index ba5f9709bced..6a897788f5a7 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -463,6 +463,16 @@ sub parse_file close $fh; } +# Checks if the actual path name is leaking a kernel address. +sub check_path_for_leaks +{ + my ($path) = @_; + + if (may_leak_address($path)) { + printf("Path name may contain address: $path\n"); + } +} + # Recursively walk directory tree. sub walk { @@ -485,6 +495,8 @@ sub walk next if (skip($path)); + check_path_for_leaks($path); + if (-d $path) { push @dirs, $path; next; -- cgit v1.2.3-59-g8ed1b From e875d33d7f06d1107c057d12bb5aaba84738e418 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 26 Feb 2018 09:27:59 +1100 Subject: MAINTAINERS: Update LEAKING_ADDRESSES MAINTAINERS is out of date for leaking_addresses.pl. There is now a tree on kernel.org for development of this script. We have a second maintainer now, thanks Tycho. Development of this scripts was started on kernel-hardening mailing list so let's keep it there. Update maintainer details; Add mailing list, kernel.org hosted tree, and second maintainer. Signed-off-by: Tobin C. Harding --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6e950b8b4a41..fef8f4ad7c3e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7910,7 +7910,10 @@ F: drivers/scsi/53c700* LEAKING_ADDRESSES M: Tobin C. Harding +M: Tycho Andersen +L: kernel-hardening@lists.openwall.com S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tobin/leaks.git F: scripts/leaking_addresses.pl LED SUBSYSTEM -- cgit v1.2.3-59-g8ed1b