1136fc5c4STobin C. Harding#!/usr/bin/env perl 2136fc5c4STobin C. Harding# 3136fc5c4STobin C. Harding# (c) 2017 Tobin C. Harding <me@tobin.cc> 4136fc5c4STobin C. Harding# Licensed under the terms of the GNU GPL License version 2 5136fc5c4STobin C. Harding# 6136fc5c4STobin C. Harding# leaking_addresses.pl: Scan 64 bit kernel for potential leaking addresses. 7136fc5c4STobin C. Harding# - Scans dmesg output. 8136fc5c4STobin C. Harding# - Walks directory tree and parses each file (for each directory in @DIRS). 9136fc5c4STobin C. Harding# 10136fc5c4STobin C. Harding# You can configure the behaviour of the script; 11136fc5c4STobin C. Harding# 12136fc5c4STobin C. Harding# - By adding paths, for directories you do not want to walk; 13136fc5c4STobin C. Harding# absolute paths: @skip_walk_dirs_abs 14136fc5c4STobin C. Harding# directory names: @skip_walk_dirs_any 15136fc5c4STobin C. Harding# 16136fc5c4STobin C. Harding# - By adding paths, for files you do not want to parse; 17136fc5c4STobin C. Harding# absolute paths: @skip_parse_files_abs 18136fc5c4STobin C. Harding# file names: @skip_parse_files_any 19136fc5c4STobin C. Harding# 20136fc5c4STobin C. Harding# The use of @skip_xxx_xxx_any causes files to be skipped where ever they occur. 21136fc5c4STobin C. Harding# For example adding 'fd' to @skip_walk_dirs_any causes the fd/ directory to be 22136fc5c4STobin C. Harding# skipped for all PID sub-directories of /proc 23136fc5c4STobin C. Harding# 24136fc5c4STobin C. Harding# The same thing can be achieved by passing command line options to --dont-walk 25136fc5c4STobin C. Harding# and --dont-parse. If absolute paths are supplied to these options they are 26136fc5c4STobin C. Harding# appended to the @skip_xxx_xxx_abs arrays. If file names are supplied to these 27136fc5c4STobin C. Harding# options, they are appended to the @skip_xxx_xxx_any arrays. 28136fc5c4STobin C. Harding# 29136fc5c4STobin C. Harding# Use --debug to output path before parsing, this is useful to find files that 30136fc5c4STobin C. Harding# cause the script to choke. 31136fc5c4STobin C. Harding# 32136fc5c4STobin C. Harding# You may like to set kptr_restrict=2 before running script 33136fc5c4STobin C. Harding# (see Documentation/sysctl/kernel.txt). 34136fc5c4STobin C. Harding 35136fc5c4STobin C. Hardinguse warnings; 36136fc5c4STobin C. Hardinguse strict; 37136fc5c4STobin C. Hardinguse POSIX; 38136fc5c4STobin C. Hardinguse File::Basename; 39136fc5c4STobin C. Hardinguse File::Spec; 40136fc5c4STobin C. Hardinguse Cwd 'abs_path'; 41136fc5c4STobin C. Hardinguse Term::ANSIColor qw(:constants); 42136fc5c4STobin C. Hardinguse Getopt::Long qw(:config no_auto_abbrev); 43136fc5c4STobin C. Harding 44136fc5c4STobin C. Hardingmy $P = $0; 45136fc5c4STobin C. Hardingmy $V = '0.01'; 46136fc5c4STobin C. Harding 47136fc5c4STobin C. Harding# Directories to scan. 48136fc5c4STobin C. Hardingmy @DIRS = ('/proc', '/sys'); 49136fc5c4STobin C. Harding 50136fc5c4STobin C. Harding# Command line options. 51136fc5c4STobin C. Hardingmy $help = 0; 52136fc5c4STobin C. Hardingmy $debug = 0; 53136fc5c4STobin C. Hardingmy @dont_walk = (); 54136fc5c4STobin C. Hardingmy @dont_parse = (); 55136fc5c4STobin C. Harding 56136fc5c4STobin C. Harding# Do not parse these files (absolute path). 57136fc5c4STobin C. Hardingmy @skip_parse_files_abs = ('/proc/kmsg', 58136fc5c4STobin C. Harding '/proc/kcore', 59136fc5c4STobin C. Harding '/proc/fs/ext4/sdb1/mb_groups', 60136fc5c4STobin C. Harding '/proc/1/fd/3', 61136fc5c4STobin C. Harding '/sys/kernel/debug/tracing/trace_pipe', 62136fc5c4STobin C. Harding '/sys/kernel/security/apparmor/revision'); 63136fc5c4STobin C. Harding 64136fc5c4STobin C. Harding# Do not parse thes files under any subdirectory. 65136fc5c4STobin C. Hardingmy @skip_parse_files_any = ('0', 66136fc5c4STobin C. Harding '1', 67136fc5c4STobin C. Harding '2', 68136fc5c4STobin C. Harding 'pagemap', 69136fc5c4STobin C. Harding 'events', 70136fc5c4STobin C. Harding 'access', 71136fc5c4STobin C. Harding 'registers', 72136fc5c4STobin C. Harding 'snapshot_raw', 73136fc5c4STobin C. Harding 'trace_pipe_raw', 74136fc5c4STobin C. Harding 'ptmx', 75136fc5c4STobin C. Harding 'trace_pipe'); 76136fc5c4STobin C. Harding 77136fc5c4STobin C. Harding# Do not walk these directories (absolute path). 78136fc5c4STobin C. Hardingmy @skip_walk_dirs_abs = (); 79136fc5c4STobin C. Harding 80136fc5c4STobin C. Harding# Do not walk these directories under any subdirectory. 81136fc5c4STobin C. Hardingmy @skip_walk_dirs_any = ('self', 82136fc5c4STobin C. Harding 'thread-self', 83136fc5c4STobin C. Harding 'cwd', 84136fc5c4STobin C. Harding 'fd', 85136fc5c4STobin C. Harding 'stderr', 86136fc5c4STobin C. Harding 'stdin', 87136fc5c4STobin C. Harding 'stdout'); 88136fc5c4STobin C. Harding 89136fc5c4STobin C. Hardingsub help 90136fc5c4STobin C. Harding{ 91136fc5c4STobin C. Harding my ($exitcode) = @_; 92136fc5c4STobin C. Harding 93136fc5c4STobin C. Harding print << "EOM"; 94136fc5c4STobin C. HardingUsage: $P [OPTIONS] 95136fc5c4STobin C. HardingVersion: $V 96136fc5c4STobin C. Harding 97136fc5c4STobin C. HardingOptions: 98136fc5c4STobin C. Harding 99136fc5c4STobin C. Harding --dont-walk=<dir> Don't walk tree starting at <dir>. 100136fc5c4STobin C. Harding --dont-parse=<file> Don't parse <file>. 101136fc5c4STobin C. Harding -d, --debug Display debugging output. 102136fc5c4STobin C. Harding -h, --help, --version Display this help and exit. 103136fc5c4STobin C. Harding 104136fc5c4STobin C. HardingIf an absolute path is passed to --dont_XXX then this path is skipped. If a 105136fc5c4STobin C. Hardingsingle filename is passed then this file/directory will be skipped when 106136fc5c4STobin C. Hardingappearing under any subdirectory. 107136fc5c4STobin C. Harding 108136fc5c4STobin C. HardingExample: 109136fc5c4STobin C. Harding 110136fc5c4STobin C. Harding # Just scan dmesg output. 111136fc5c4STobin C. Harding scripts/leaking_addresses.pl --dont_walk_abs /proc --dont_walk_abs /sys 112136fc5c4STobin C. Harding 113136fc5c4STobin C. HardingScans the running (64 bit) kernel for potential leaking addresses. 114136fc5c4STobin C. Harding 115136fc5c4STobin C. HardingEOM 116136fc5c4STobin C. Harding exit($exitcode); 117136fc5c4STobin C. Harding} 118136fc5c4STobin C. Harding 119136fc5c4STobin C. HardingGetOptions( 120136fc5c4STobin C. Harding 'dont-walk=s' => \@dont_walk, 121136fc5c4STobin C. Harding 'dont-parse=s' => \@dont_parse, 122136fc5c4STobin C. Harding 'd|debug' => \$debug, 123136fc5c4STobin C. Harding 'h|help' => \$help, 124136fc5c4STobin C. Harding 'version' => \$help 125136fc5c4STobin C. Harding) or help(1); 126136fc5c4STobin C. Harding 127136fc5c4STobin C. Hardinghelp(0) if ($help); 128136fc5c4STobin C. Harding 129136fc5c4STobin C. Hardingpush_to_global(); 130136fc5c4STobin C. Harding 131136fc5c4STobin C. Hardingparse_dmesg(); 132136fc5c4STobin C. Hardingwalk(@DIRS); 133136fc5c4STobin C. Harding 134136fc5c4STobin C. Hardingexit 0; 135136fc5c4STobin C. Harding 136136fc5c4STobin C. Hardingsub debug_arrays 137136fc5c4STobin C. Harding{ 138136fc5c4STobin C. Harding print 'dirs_any: ' . join(", ", @skip_walk_dirs_any) . "\n"; 139136fc5c4STobin C. Harding print 'dirs_abs: ' . join(", ", @skip_walk_dirs_abs) . "\n"; 140136fc5c4STobin C. Harding print 'parse_any: ' . join(", ", @skip_parse_files_any) . "\n"; 141136fc5c4STobin C. Harding print 'parse_abs: ' . join(", ", @skip_parse_files_abs) . "\n"; 142136fc5c4STobin C. Harding} 143136fc5c4STobin C. Harding 144136fc5c4STobin C. Hardingsub dprint 145136fc5c4STobin C. Harding{ 146136fc5c4STobin C. Harding printf(STDERR @_) if $debug; 147136fc5c4STobin C. Harding} 148136fc5c4STobin C. Harding 149136fc5c4STobin C. Hardingsub push_in_abs_any 150136fc5c4STobin C. Harding{ 151136fc5c4STobin C. Harding my ($in, $abs, $any) = @_; 152136fc5c4STobin C. Harding 153136fc5c4STobin C. Harding foreach my $path (@$in) { 154136fc5c4STobin C. Harding if (File::Spec->file_name_is_absolute($path)) { 155136fc5c4STobin C. Harding push @$abs, $path; 156136fc5c4STobin C. Harding } elsif (index($path,'/') == -1) { 157136fc5c4STobin C. Harding push @$any, $path; 158136fc5c4STobin C. Harding } else { 159136fc5c4STobin C. Harding print 'path error: ' . $path; 160136fc5c4STobin C. Harding } 161136fc5c4STobin C. Harding } 162136fc5c4STobin C. Harding} 163136fc5c4STobin C. Harding 164136fc5c4STobin C. Harding# Push command line options to global arrays. 165136fc5c4STobin C. Hardingsub push_to_global 166136fc5c4STobin C. Harding{ 167136fc5c4STobin C. Harding push_in_abs_any(\@dont_walk, \@skip_walk_dirs_abs, \@skip_walk_dirs_any); 168136fc5c4STobin C. Harding push_in_abs_any(\@dont_parse, \@skip_parse_files_abs, \@skip_parse_files_any); 169136fc5c4STobin C. Harding} 170136fc5c4STobin C. Harding 171136fc5c4STobin C. Hardingsub is_false_positive 172136fc5c4STobin C. Harding{ 173136fc5c4STobin C. Harding my ($match) = @_; 174136fc5c4STobin C. Harding 175136fc5c4STobin C. Harding if ($match =~ '\b(0x)?(f|F){16}\b' or 176136fc5c4STobin C. Harding $match =~ '\b(0x)?0{16}\b') { 177136fc5c4STobin C. Harding return 1; 178136fc5c4STobin C. Harding } 179136fc5c4STobin C. Harding 1807e5758f7STobin C. Harding 1817e5758f7STobin C. Harding if ($match =~ '\bf{10}600000\b' or# vsyscall memory region, we should probably check against a range here. 182136fc5c4STobin C. Harding $match =~ '\bf{10}601000\b') { 183136fc5c4STobin C. Harding return 1; 184136fc5c4STobin C. Harding } 185136fc5c4STobin C. Harding 186136fc5c4STobin C. Harding return 0; 187136fc5c4STobin C. Harding} 188136fc5c4STobin C. Harding 189136fc5c4STobin C. Harding# True if argument potentially contains a kernel address. 190136fc5c4STobin C. Hardingsub may_leak_address 191136fc5c4STobin C. Harding{ 192136fc5c4STobin C. Harding my ($line) = @_; 193136fc5c4STobin C. Harding my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b'; 194136fc5c4STobin C. Harding 195136fc5c4STobin C. Harding # Signal masks. 196136fc5c4STobin C. Harding if ($line =~ '^SigBlk:' or 197136fc5c4STobin C. Harding $line =~ '^SigCgt:') { 198136fc5c4STobin C. Harding return 0; 199136fc5c4STobin C. Harding } 200136fc5c4STobin C. Harding 201136fc5c4STobin C. Harding if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or 202136fc5c4STobin C. Harding $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') { 203136fc5c4STobin C. Harding return 0; 204136fc5c4STobin C. Harding } 205136fc5c4STobin C. Harding 206136fc5c4STobin C. Harding while (/($address)/g) { 207136fc5c4STobin C. Harding if (!is_false_positive($1)) { 208136fc5c4STobin C. Harding return 1; 209136fc5c4STobin C. Harding } 210136fc5c4STobin C. Harding } 211136fc5c4STobin C. Harding 212136fc5c4STobin C. Harding return 0; 213136fc5c4STobin C. Harding} 214136fc5c4STobin C. Harding 215136fc5c4STobin C. Hardingsub parse_dmesg 216136fc5c4STobin C. Harding{ 217136fc5c4STobin C. Harding open my $cmd, '-|', 'dmesg'; 218136fc5c4STobin C. Harding while (<$cmd>) { 219136fc5c4STobin C. Harding if (may_leak_address($_)) { 220136fc5c4STobin C. Harding print 'dmesg: ' . $_; 221136fc5c4STobin C. Harding } 222136fc5c4STobin C. Harding } 223136fc5c4STobin C. Harding close $cmd; 224136fc5c4STobin C. Harding} 225136fc5c4STobin C. Harding 226136fc5c4STobin C. Harding# True if we should skip this path. 227136fc5c4STobin C. Hardingsub skip 228136fc5c4STobin C. Harding{ 229136fc5c4STobin C. Harding my ($path, $paths_abs, $paths_any) = @_; 230136fc5c4STobin C. Harding 231136fc5c4STobin C. Harding foreach (@$paths_abs) { 232136fc5c4STobin C. Harding return 1 if (/^$path$/); 233136fc5c4STobin C. Harding } 234136fc5c4STobin C. Harding 235136fc5c4STobin C. Harding my($filename, $dirs, $suffix) = fileparse($path); 236136fc5c4STobin C. Harding foreach (@$paths_any) { 237136fc5c4STobin C. Harding return 1 if (/^$filename$/); 238136fc5c4STobin C. Harding } 239136fc5c4STobin C. Harding 240136fc5c4STobin C. Harding return 0; 241136fc5c4STobin C. Harding} 242136fc5c4STobin C. Harding 243136fc5c4STobin C. Hardingsub skip_parse 244136fc5c4STobin C. Harding{ 245136fc5c4STobin C. Harding my ($path) = @_; 246136fc5c4STobin C. Harding return skip($path, \@skip_parse_files_abs, \@skip_parse_files_any); 247136fc5c4STobin C. Harding} 248136fc5c4STobin C. Harding 249136fc5c4STobin C. Hardingsub parse_file 250136fc5c4STobin C. Harding{ 251136fc5c4STobin C. Harding my ($file) = @_; 252136fc5c4STobin C. Harding 253136fc5c4STobin C. Harding if (! -R $file) { 254136fc5c4STobin C. Harding return; 255136fc5c4STobin C. Harding } 256136fc5c4STobin C. Harding 257136fc5c4STobin C. Harding if (skip_parse($file)) { 258136fc5c4STobin C. Harding dprint "skipping file: $file\n"; 259136fc5c4STobin C. Harding return; 260136fc5c4STobin C. Harding } 261136fc5c4STobin C. Harding dprint "parsing: $file\n"; 262136fc5c4STobin C. Harding 263136fc5c4STobin C. Harding open my $fh, "<", $file or return; 264136fc5c4STobin C. Harding while ( <$fh> ) { 265136fc5c4STobin C. Harding if (may_leak_address($_)) { 266136fc5c4STobin C. Harding print $file . ': ' . $_; 267136fc5c4STobin C. Harding } 268136fc5c4STobin C. Harding } 269136fc5c4STobin C. Harding close $fh; 270136fc5c4STobin C. Harding} 271136fc5c4STobin C. Harding 272136fc5c4STobin C. Harding 273136fc5c4STobin C. Harding# True if we should skip walking this directory. 274136fc5c4STobin C. Hardingsub skip_walk 275136fc5c4STobin C. Harding{ 276136fc5c4STobin C. Harding my ($path) = @_; 277136fc5c4STobin C. Harding return skip($path, \@skip_walk_dirs_abs, \@skip_walk_dirs_any) 278136fc5c4STobin C. Harding} 279136fc5c4STobin C. Harding 280136fc5c4STobin C. Harding# Recursively walk directory tree. 281136fc5c4STobin C. Hardingsub walk 282136fc5c4STobin C. Harding{ 283136fc5c4STobin C. Harding my @dirs = @_; 284136fc5c4STobin C. Harding my %seen; 285136fc5c4STobin C. Harding 286136fc5c4STobin C. Harding while (my $pwd = shift @dirs) { 287136fc5c4STobin C. Harding next if (skip_walk($pwd)); 288136fc5c4STobin C. Harding next if (!opendir(DIR, $pwd)); 289136fc5c4STobin C. Harding my @files = readdir(DIR); 290136fc5c4STobin C. Harding closedir(DIR); 291136fc5c4STobin C. Harding 292136fc5c4STobin C. Harding foreach my $file (@files) { 293136fc5c4STobin C. Harding next if ($file eq '.' or $file eq '..'); 294136fc5c4STobin C. Harding 295136fc5c4STobin C. Harding my $path = "$pwd/$file"; 296136fc5c4STobin C. Harding next if (-l $path); 297136fc5c4STobin C. Harding 298136fc5c4STobin C. Harding if (-d $path) { 299136fc5c4STobin C. Harding push @dirs, $path; 300136fc5c4STobin C. Harding } else { 301136fc5c4STobin C. Harding parse_file($path); 302136fc5c4STobin C. Harding } 303136fc5c4STobin C. Harding } 304136fc5c4STobin C. Harding } 305136fc5c4STobin C. Harding} 306