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