18c2ecf20Sopenharmony_ci#!/usr/bin/env perl
28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only
38c2ecf20Sopenharmony_ci#
48c2ecf20Sopenharmony_ci# (c) 2017 Tobin C. Harding <me@tobin.cc>
58c2ecf20Sopenharmony_ci#
68c2ecf20Sopenharmony_ci# leaking_addresses.pl: Scan the kernel for potential leaking addresses.
78c2ecf20Sopenharmony_ci#  - Scans dmesg output.
88c2ecf20Sopenharmony_ci#  - Walks directory tree and parses each file (for each directory in @DIRS).
98c2ecf20Sopenharmony_ci#
108c2ecf20Sopenharmony_ci# Use --debug to output path before parsing, this is useful to find files that
118c2ecf20Sopenharmony_ci# cause the script to choke.
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#
148c2ecf20Sopenharmony_ci# When the system is idle it is likely that most files under /proc/PID will be
158c2ecf20Sopenharmony_ci# identical for various processes.  Scanning _all_ the PIDs under /proc is
168c2ecf20Sopenharmony_ci# unnecessary and implies that we are thoroughly scanning /proc.  This is _not_
178c2ecf20Sopenharmony_ci# the case because there may be ways userspace can trigger creation of /proc
188c2ecf20Sopenharmony_ci# files that leak addresses but were not present during a scan.  For these two
198c2ecf20Sopenharmony_ci# reasons we exclude all PID directories under /proc except '1/'
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ciuse warnings;
228c2ecf20Sopenharmony_ciuse strict;
238c2ecf20Sopenharmony_ciuse POSIX;
248c2ecf20Sopenharmony_ciuse File::Basename;
258c2ecf20Sopenharmony_ciuse File::Spec;
268c2ecf20Sopenharmony_ciuse Cwd 'abs_path';
278c2ecf20Sopenharmony_ciuse Term::ANSIColor qw(:constants);
288c2ecf20Sopenharmony_ciuse Getopt::Long qw(:config no_auto_abbrev);
298c2ecf20Sopenharmony_ciuse Config;
308c2ecf20Sopenharmony_ciuse bigint qw/hex/;
318c2ecf20Sopenharmony_ciuse feature 'state';
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cimy $P = $0;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci# Directories to scan.
368c2ecf20Sopenharmony_cimy @DIRS = ('/proc', '/sys');
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci# Timer for parsing each file, in seconds.
398c2ecf20Sopenharmony_cimy $TIMEOUT = 10;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci# Kernel addresses vary by architecture.  We can only auto-detect the following
428c2ecf20Sopenharmony_ci# architectures (using `uname -m`).  (flag --32-bit overrides auto-detection.)
438c2ecf20Sopenharmony_cimy @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64', 'x86');
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci# Command line options.
468c2ecf20Sopenharmony_cimy $help = 0;
478c2ecf20Sopenharmony_cimy $debug = 0;
488c2ecf20Sopenharmony_cimy $raw = 0;
498c2ecf20Sopenharmony_cimy $output_raw = "";	# Write raw results to file.
508c2ecf20Sopenharmony_cimy $input_raw = "";	# Read raw results from file instead of scanning.
518c2ecf20Sopenharmony_cimy $suppress_dmesg = 0;		# Don't show dmesg in output.
528c2ecf20Sopenharmony_cimy $squash_by_path = 0;		# Summary report grouped by absolute path.
538c2ecf20Sopenharmony_cimy $squash_by_filename = 0;	# Summary report grouped by filename.
548c2ecf20Sopenharmony_cimy $kernel_config_file = "";	# Kernel configuration file.
558c2ecf20Sopenharmony_cimy $opt_32bit = 0;		# Scan 32-bit kernel.
568c2ecf20Sopenharmony_cimy $page_offset_32bit = 0;	# Page offset for 32-bit kernel.
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci# Skip these absolute paths.
598c2ecf20Sopenharmony_cimy @skip_abs = (
608c2ecf20Sopenharmony_ci	'/proc/kmsg',
618c2ecf20Sopenharmony_ci	'/proc/device-tree',
628c2ecf20Sopenharmony_ci	'/proc/1/syscall',
638c2ecf20Sopenharmony_ci	'/sys/firmware/devicetree',
648c2ecf20Sopenharmony_ci	'/sys/kernel/debug/tracing/trace_pipe',
658c2ecf20Sopenharmony_ci	'/sys/kernel/security/apparmor/revision');
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci# Skip these under any subdirectory.
688c2ecf20Sopenharmony_cimy @skip_any = (
698c2ecf20Sopenharmony_ci	'pagemap',
708c2ecf20Sopenharmony_ci	'events',
718c2ecf20Sopenharmony_ci	'access',
728c2ecf20Sopenharmony_ci	'registers',
738c2ecf20Sopenharmony_ci	'snapshot_raw',
748c2ecf20Sopenharmony_ci	'trace_pipe_raw',
758c2ecf20Sopenharmony_ci	'ptmx',
768c2ecf20Sopenharmony_ci	'trace_pipe',
778c2ecf20Sopenharmony_ci	'fd',
788c2ecf20Sopenharmony_ci	'usbmon');
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cisub help
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	my ($exitcode) = @_;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	print << "EOM";
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciUsage: $P [OPTIONS]
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ciOptions:
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	-o, --output-raw=<file>		Save results for future processing.
918c2ecf20Sopenharmony_ci	-i, --input-raw=<file>		Read results from file instead of scanning.
928c2ecf20Sopenharmony_ci	      --raw			Show raw results (default).
938c2ecf20Sopenharmony_ci	      --suppress-dmesg		Do not show dmesg results.
948c2ecf20Sopenharmony_ci	      --squash-by-path		Show one result per unique path.
958c2ecf20Sopenharmony_ci	      --squash-by-filename	Show one result per unique filename.
968c2ecf20Sopenharmony_ci	--kernel-config-file=<file>     Kernel configuration file (e.g /boot/config)
978c2ecf20Sopenharmony_ci	--32-bit			Scan 32-bit kernel.
988c2ecf20Sopenharmony_ci	--page-offset-32-bit=o		Page offset (for 32-bit kernel 0xABCD1234).
998c2ecf20Sopenharmony_ci	-d, --debug			Display debugging output.
1008c2ecf20Sopenharmony_ci	-h, --help			Display this help and exit.
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ciScans the running kernel for potential leaking addresses.
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciEOM
1058c2ecf20Sopenharmony_ci	exit($exitcode);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciGetOptions(
1098c2ecf20Sopenharmony_ci	'd|debug'		=> \$debug,
1108c2ecf20Sopenharmony_ci	'h|help'		=> \$help,
1118c2ecf20Sopenharmony_ci	'o|output-raw=s'        => \$output_raw,
1128c2ecf20Sopenharmony_ci	'i|input-raw=s'         => \$input_raw,
1138c2ecf20Sopenharmony_ci	'suppress-dmesg'        => \$suppress_dmesg,
1148c2ecf20Sopenharmony_ci	'squash-by-path'        => \$squash_by_path,
1158c2ecf20Sopenharmony_ci	'squash-by-filename'    => \$squash_by_filename,
1168c2ecf20Sopenharmony_ci	'raw'                   => \$raw,
1178c2ecf20Sopenharmony_ci	'kernel-config-file=s'	=> \$kernel_config_file,
1188c2ecf20Sopenharmony_ci	'32-bit'		=> \$opt_32bit,
1198c2ecf20Sopenharmony_ci	'page-offset-32-bit=o'	=> \$page_offset_32bit,
1208c2ecf20Sopenharmony_ci) or help(1);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cihelp(0) if ($help);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciif ($input_raw) {
1258c2ecf20Sopenharmony_ci	format_output($input_raw);
1268c2ecf20Sopenharmony_ci	exit(0);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciif (!$input_raw and ($squash_by_path or $squash_by_filename)) {
1308c2ecf20Sopenharmony_ci	printf "\nSummary reporting only available with --input-raw=<file>\n";
1318c2ecf20Sopenharmony_ci	printf "(First run scan with --output-raw=<file>.)\n";
1328c2ecf20Sopenharmony_ci	exit(128);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciif (!(is_supported_architecture() or $opt_32bit or $page_offset_32bit)) {
1368c2ecf20Sopenharmony_ci	printf "\nScript does not support your architecture, sorry.\n";
1378c2ecf20Sopenharmony_ci	printf "\nCurrently we support: \n\n";
1388c2ecf20Sopenharmony_ci	foreach(@SUPPORTED_ARCHITECTURES) {
1398c2ecf20Sopenharmony_ci		printf "\t%s\n", $_;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	printf("\n");
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	printf("If you are running a 32-bit architecture you may use:\n");
1448c2ecf20Sopenharmony_ci	printf("\n\t--32-bit or --page-offset-32-bit=<page offset>\n\n");
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	my $archname = `uname -m`;
1478c2ecf20Sopenharmony_ci	printf("Machine hardware name (`uname -m`): %s\n", $archname);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	exit(129);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciif ($output_raw) {
1538c2ecf20Sopenharmony_ci	open my $fh, '>', $output_raw or die "$0: $output_raw: $!\n";
1548c2ecf20Sopenharmony_ci	select $fh;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciparse_dmesg();
1588c2ecf20Sopenharmony_ciwalk(@DIRS);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ciexit 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cisub dprint
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	printf(STDERR @_) if $debug;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cisub is_supported_architecture
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	return (is_x86_64() or is_ppc64() or is_ix86_32());
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cisub is_32bit
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	# Allow --32-bit or --page-offset-32-bit to override
1758c2ecf20Sopenharmony_ci	if ($opt_32bit or $page_offset_32bit) {
1768c2ecf20Sopenharmony_ci		return 1;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return is_ix86_32();
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cisub is_ix86_32
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci       state $arch = `uname -m`;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci       chomp $arch;
1878c2ecf20Sopenharmony_ci       if ($arch =~ m/i[3456]86/) {
1888c2ecf20Sopenharmony_ci               return 1;
1898c2ecf20Sopenharmony_ci       }
1908c2ecf20Sopenharmony_ci       return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cisub is_arch
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci       my ($desc) = @_;
1968c2ecf20Sopenharmony_ci       my $arch = `uname -m`;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci       chomp $arch;
1998c2ecf20Sopenharmony_ci       if ($arch eq $desc) {
2008c2ecf20Sopenharmony_ci               return 1;
2018c2ecf20Sopenharmony_ci       }
2028c2ecf20Sopenharmony_ci       return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cisub is_x86_64
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	state $is = is_arch('x86_64');
2088c2ecf20Sopenharmony_ci	return $is;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cisub is_ppc64
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	state $is = is_arch('ppc64');
2148c2ecf20Sopenharmony_ci	return $is;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci# Gets config option value from kernel config file.
2188c2ecf20Sopenharmony_ci# Returns "" on error or if config option not found.
2198c2ecf20Sopenharmony_cisub get_kernel_config_option
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	my ($option) = @_;
2228c2ecf20Sopenharmony_ci	my $value = "";
2238c2ecf20Sopenharmony_ci	my $tmp_file = "";
2248c2ecf20Sopenharmony_ci	my @config_files;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	# Allow --kernel-config-file to override.
2278c2ecf20Sopenharmony_ci	if ($kernel_config_file ne "") {
2288c2ecf20Sopenharmony_ci		@config_files = ($kernel_config_file);
2298c2ecf20Sopenharmony_ci	} elsif (-R "/proc/config.gz") {
2308c2ecf20Sopenharmony_ci		my $tmp_file = "/tmp/tmpkconf";
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		if (system("gunzip < /proc/config.gz > $tmp_file")) {
2338c2ecf20Sopenharmony_ci			dprint("system(gunzip < /proc/config.gz) failed\n");
2348c2ecf20Sopenharmony_ci			return "";
2358c2ecf20Sopenharmony_ci		} else {
2368c2ecf20Sopenharmony_ci			@config_files = ($tmp_file);
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci	} else {
2398c2ecf20Sopenharmony_ci		my $file = '/boot/config-' . `uname -r`;
2408c2ecf20Sopenharmony_ci		chomp $file;
2418c2ecf20Sopenharmony_ci		@config_files = ($file, '/boot/config');
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	foreach my $file (@config_files) {
2458c2ecf20Sopenharmony_ci		dprint("parsing config file: $file\n");
2468c2ecf20Sopenharmony_ci		$value = option_from_file($option, $file);
2478c2ecf20Sopenharmony_ci		if ($value ne "") {
2488c2ecf20Sopenharmony_ci			last;
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if ($tmp_file ne "") {
2538c2ecf20Sopenharmony_ci		system("rm -f $tmp_file");
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return $value;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci# Parses $file and returns kernel configuration option value.
2608c2ecf20Sopenharmony_cisub option_from_file
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	my ($option, $file) = @_;
2638c2ecf20Sopenharmony_ci	my $str = "";
2648c2ecf20Sopenharmony_ci	my $val = "";
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	open(my $fh, "<", $file) or return "";
2678c2ecf20Sopenharmony_ci	while (my $line = <$fh> ) {
2688c2ecf20Sopenharmony_ci		if ($line =~ /^$option/) {
2698c2ecf20Sopenharmony_ci			($str, $val) = split /=/, $line;
2708c2ecf20Sopenharmony_ci			chomp $val;
2718c2ecf20Sopenharmony_ci			last;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	close $fh;
2768c2ecf20Sopenharmony_ci	return $val;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cisub is_false_positive
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	my ($match) = @_;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (is_32bit()) {
2848c2ecf20Sopenharmony_ci		return is_false_positive_32bit($match);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	# 64 bit false positives.
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if ($match =~ '\b(0x)?(f|F){16}\b' or
2908c2ecf20Sopenharmony_ci	    $match =~ '\b(0x)?0{16}\b') {
2918c2ecf20Sopenharmony_ci		return 1;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (is_x86_64() and is_in_vsyscall_memory_region($match)) {
2958c2ecf20Sopenharmony_ci		return 1;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cisub is_false_positive_32bit
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci       my ($match) = @_;
3048c2ecf20Sopenharmony_ci       state $page_offset = get_page_offset();
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci       if ($match =~ '\b(0x)?(f|F){8}\b') {
3078c2ecf20Sopenharmony_ci               return 1;
3088c2ecf20Sopenharmony_ci       }
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci       if (hex($match) < $page_offset) {
3118c2ecf20Sopenharmony_ci               return 1;
3128c2ecf20Sopenharmony_ci       }
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci       return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci# returns integer value
3188c2ecf20Sopenharmony_cisub get_page_offset
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci       my $page_offset;
3218c2ecf20Sopenharmony_ci       my $default_offset = 0xc0000000;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci       # Allow --page-offset-32bit to override.
3248c2ecf20Sopenharmony_ci       if ($page_offset_32bit != 0) {
3258c2ecf20Sopenharmony_ci               return $page_offset_32bit;
3268c2ecf20Sopenharmony_ci       }
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci       $page_offset = get_kernel_config_option('CONFIG_PAGE_OFFSET');
3298c2ecf20Sopenharmony_ci       if (!$page_offset) {
3308c2ecf20Sopenharmony_ci	       return $default_offset;
3318c2ecf20Sopenharmony_ci       }
3328c2ecf20Sopenharmony_ci       return $page_offset;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cisub is_in_vsyscall_memory_region
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	my ($match) = @_;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	my $hex = hex($match);
3408c2ecf20Sopenharmony_ci	my $region_min = hex("0xffffffffff600000");
3418c2ecf20Sopenharmony_ci	my $region_max = hex("0xffffffffff601000");
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return ($hex >= $region_min and $hex <= $region_max);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci# True if argument potentially contains a kernel address.
3478c2ecf20Sopenharmony_cisub may_leak_address
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	my ($line) = @_;
3508c2ecf20Sopenharmony_ci	my $address_re;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	# Signal masks.
3538c2ecf20Sopenharmony_ci	if ($line =~ '^SigBlk:' or
3548c2ecf20Sopenharmony_ci	    $line =~ '^SigIgn:' or
3558c2ecf20Sopenharmony_ci	    $line =~ '^SigCgt:') {
3568c2ecf20Sopenharmony_ci		return 0;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
3608c2ecf20Sopenharmony_ci	    $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {
3618c2ecf20Sopenharmony_ci		return 0;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	$address_re = get_address_re();
3658c2ecf20Sopenharmony_ci	while ($line =~ /($address_re)/g) {
3668c2ecf20Sopenharmony_ci		if (!is_false_positive($1)) {
3678c2ecf20Sopenharmony_ci			return 1;
3688c2ecf20Sopenharmony_ci		}
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return 0;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cisub get_address_re
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	if (is_ppc64()) {
3778c2ecf20Sopenharmony_ci		return '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b';
3788c2ecf20Sopenharmony_ci	} elsif (is_32bit()) {
3798c2ecf20Sopenharmony_ci		return '\b(0x)?[[:xdigit:]]{8}\b';
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return get_x86_64_re();
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cisub get_x86_64_re
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	# We handle page table levels but only if explicitly configured using
3888c2ecf20Sopenharmony_ci	# CONFIG_PGTABLE_LEVELS.  If config file parsing fails or config option
3898c2ecf20Sopenharmony_ci	# is not found we default to using address regular expression suitable
3908c2ecf20Sopenharmony_ci	# for 4 page table levels.
3918c2ecf20Sopenharmony_ci	state $ptl = get_kernel_config_option('CONFIG_PGTABLE_LEVELS');
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if ($ptl == 5) {
3948c2ecf20Sopenharmony_ci		return '\b(0x)?ff[[:xdigit:]]{14}\b';
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci	return '\b(0x)?ffff[[:xdigit:]]{12}\b';
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cisub parse_dmesg
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	open my $cmd, '-|', 'dmesg';
4028c2ecf20Sopenharmony_ci	while (<$cmd>) {
4038c2ecf20Sopenharmony_ci		if (may_leak_address($_)) {
4048c2ecf20Sopenharmony_ci			print 'dmesg: ' . $_;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	close $cmd;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci# True if we should skip this path.
4118c2ecf20Sopenharmony_cisub skip
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	my ($path) = @_;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	foreach (@skip_abs) {
4168c2ecf20Sopenharmony_ci		return 1 if (/^$path$/);
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	my($filename, $dirs, $suffix) = fileparse($path);
4208c2ecf20Sopenharmony_ci	foreach (@skip_any) {
4218c2ecf20Sopenharmony_ci		return 1 if (/^$filename$/);
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return 0;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cisub timed_parse_file
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	my ($file) = @_;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	eval {
4328c2ecf20Sopenharmony_ci		local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required.
4338c2ecf20Sopenharmony_ci		alarm $TIMEOUT;
4348c2ecf20Sopenharmony_ci		parse_file($file);
4358c2ecf20Sopenharmony_ci		alarm 0;
4368c2ecf20Sopenharmony_ci	};
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if ($@) {
4398c2ecf20Sopenharmony_ci		die unless $@ eq "alarm\n";	# Propagate unexpected errors.
4408c2ecf20Sopenharmony_ci		printf STDERR "timed out parsing: %s\n", $file;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cisub parse_file
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	my ($file) = @_;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (! -R $file) {
4498c2ecf20Sopenharmony_ci		return;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (! -T $file) {
4538c2ecf20Sopenharmony_ci		return;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	open my $fh, "<", $file or return;
4578c2ecf20Sopenharmony_ci	while ( <$fh> ) {
4588c2ecf20Sopenharmony_ci		chomp;
4598c2ecf20Sopenharmony_ci		if (may_leak_address($_)) {
4608c2ecf20Sopenharmony_ci			printf("$file: $_\n");
4618c2ecf20Sopenharmony_ci		}
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci	close $fh;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci# Checks if the actual path name is leaking a kernel address.
4678c2ecf20Sopenharmony_cisub check_path_for_leaks
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	my ($path) = @_;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (may_leak_address($path)) {
4728c2ecf20Sopenharmony_ci		printf("Path name may contain address: $path\n");
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci# Recursively walk directory tree.
4778c2ecf20Sopenharmony_cisub walk
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	my @dirs = @_;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	while (my $pwd = shift @dirs) {
4828c2ecf20Sopenharmony_ci		next if (!opendir(DIR, $pwd));
4838c2ecf20Sopenharmony_ci		my @files = readdir(DIR);
4848c2ecf20Sopenharmony_ci		closedir(DIR);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		foreach my $file (@files) {
4878c2ecf20Sopenharmony_ci			next if ($file eq '.' or $file eq '..');
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci			my $path = "$pwd/$file";
4908c2ecf20Sopenharmony_ci			next if (-l $path);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci			# skip /proc/PID except /proc/1
4938c2ecf20Sopenharmony_ci			next if (($path =~ /^\/proc\/[0-9]+$/) &&
4948c2ecf20Sopenharmony_ci				 ($path !~ /^\/proc\/1$/));
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci			next if (skip($path));
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci			check_path_for_leaks($path);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci			if (-d $path) {
5018c2ecf20Sopenharmony_ci				push @dirs, $path;
5028c2ecf20Sopenharmony_ci				next;
5038c2ecf20Sopenharmony_ci			}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci			dprint("parsing: $path\n");
5068c2ecf20Sopenharmony_ci			timed_parse_file($path);
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cisub format_output
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	my ($file) = @_;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	# Default is to show raw results.
5168c2ecf20Sopenharmony_ci	if ($raw or (!$squash_by_path and !$squash_by_filename)) {
5178c2ecf20Sopenharmony_ci		dump_raw_output($file);
5188c2ecf20Sopenharmony_ci		return;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	my ($total, $dmesg, $paths, $files) = parse_raw_file($file);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	printf "\nTotal number of results from scan (incl dmesg): %d\n", $total;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (!$suppress_dmesg) {
5268c2ecf20Sopenharmony_ci		print_dmesg($dmesg);
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if ($squash_by_filename) {
5308c2ecf20Sopenharmony_ci		squash_by($files, 'filename');
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if ($squash_by_path) {
5348c2ecf20Sopenharmony_ci		squash_by($paths, 'path');
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cisub dump_raw_output
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	my ($file) = @_;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	open (my $fh, '<', $file) or die "$0: $file: $!\n";
5438c2ecf20Sopenharmony_ci	while (<$fh>) {
5448c2ecf20Sopenharmony_ci		if ($suppress_dmesg) {
5458c2ecf20Sopenharmony_ci			if ("dmesg:" eq substr($_, 0, 6)) {
5468c2ecf20Sopenharmony_ci				next;
5478c2ecf20Sopenharmony_ci			}
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci		print $_;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci	close $fh;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cisub parse_raw_file
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	my ($file) = @_;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	my $total = 0;          # Total number of lines parsed.
5598c2ecf20Sopenharmony_ci	my @dmesg;              # dmesg output.
5608c2ecf20Sopenharmony_ci	my %files;              # Unique filenames containing leaks.
5618c2ecf20Sopenharmony_ci	my %paths;              # Unique paths containing leaks.
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	open (my $fh, '<', $file) or die "$0: $file: $!\n";
5648c2ecf20Sopenharmony_ci	while (my $line = <$fh>) {
5658c2ecf20Sopenharmony_ci		$total++;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci		if ("dmesg:" eq substr($line, 0, 6)) {
5688c2ecf20Sopenharmony_ci			push @dmesg, $line;
5698c2ecf20Sopenharmony_ci			next;
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		cache_path(\%paths, $line);
5738c2ecf20Sopenharmony_ci		cache_filename(\%files, $line);
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return $total, \@dmesg, \%paths, \%files;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cisub print_dmesg
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	my ($dmesg) = @_;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	print "\ndmesg output:\n";
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (@$dmesg == 0) {
5868c2ecf20Sopenharmony_ci		print "<no results>\n";
5878c2ecf20Sopenharmony_ci		return;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	foreach(@$dmesg) {
5918c2ecf20Sopenharmony_ci		my $index = index($_, ': ');
5928c2ecf20Sopenharmony_ci		$index += 2;    # skid ': '
5938c2ecf20Sopenharmony_ci		print substr($_, $index);
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cisub squash_by
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	my ($ref, $desc) = @_;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	print "\nResults squashed by $desc (excl dmesg). ";
6028c2ecf20Sopenharmony_ci	print "Displaying [<number of results> <$desc>], <example result>\n";
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (keys %$ref == 0) {
6058c2ecf20Sopenharmony_ci		print "<no results>\n";
6068c2ecf20Sopenharmony_ci		return;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	foreach(keys %$ref) {
6108c2ecf20Sopenharmony_ci		my $lines = $ref->{$_};
6118c2ecf20Sopenharmony_ci		my $length = @$lines;
6128c2ecf20Sopenharmony_ci		printf "[%d %s] %s", $length, $_, @$lines[0];
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cisub cache_path
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	my ($paths, $line) = @_;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	my $index = index($line, ': ');
6218c2ecf20Sopenharmony_ci	my $path = substr($line, 0, $index);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	$index += 2;            # skip ': '
6248c2ecf20Sopenharmony_ci	add_to_cache($paths, $path, substr($line, $index));
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cisub cache_filename
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	my ($files, $line) = @_;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	my $index = index($line, ': ');
6328c2ecf20Sopenharmony_ci	my $path = substr($line, 0, $index);
6338c2ecf20Sopenharmony_ci	my $filename = basename($path);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	$index += 2;            # skip ': '
6368c2ecf20Sopenharmony_ci	add_to_cache($files, $filename, substr($line, $index));
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cisub add_to_cache
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	my ($cache, $key, $value) = @_;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (!$cache->{$key}) {
6448c2ecf20Sopenharmony_ci		$cache->{$key} = ();
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci	push @{$cache->{$key}}, $value;
6478c2ecf20Sopenharmony_ci}
648