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# Use --debug to output path before parsing, this is useful to find files that 11136fc5c4STobin C. Harding# cause the script to choke. 12136fc5c4STobin C. Harding# 13136fc5c4STobin C. Harding# You may like to set kptr_restrict=2 before running script 14136fc5c4STobin C. Harding# (see Documentation/sysctl/kernel.txt). 15136fc5c4STobin C. Harding 16136fc5c4STobin C. Hardinguse warnings; 17136fc5c4STobin C. Hardinguse strict; 18136fc5c4STobin C. Hardinguse POSIX; 19136fc5c4STobin C. Hardinguse File::Basename; 20136fc5c4STobin C. Hardinguse File::Spec; 21136fc5c4STobin C. Hardinguse Cwd 'abs_path'; 22136fc5c4STobin C. Hardinguse Term::ANSIColor qw(:constants); 23136fc5c4STobin C. Hardinguse Getopt::Long qw(:config no_auto_abbrev); 24136fc5c4STobin C. Harding 25136fc5c4STobin C. Hardingmy $P = $0; 26136fc5c4STobin C. Hardingmy $V = '0.01'; 27136fc5c4STobin C. Harding 28136fc5c4STobin C. Harding# Directories to scan. 29136fc5c4STobin C. Hardingmy @DIRS = ('/proc', '/sys'); 30136fc5c4STobin C. Harding 31136fc5c4STobin C. Harding# Command line options. 32136fc5c4STobin C. Hardingmy $help = 0; 33136fc5c4STobin C. Hardingmy $debug = 0; 34136fc5c4STobin C. Harding 35136fc5c4STobin C. Harding# Do not parse these files (absolute path). 36136fc5c4STobin C. Hardingmy @skip_parse_files_abs = ('/proc/kmsg', 37136fc5c4STobin C. Harding '/proc/kcore', 38136fc5c4STobin C. Harding '/proc/fs/ext4/sdb1/mb_groups', 39136fc5c4STobin C. Harding '/proc/1/fd/3', 40136fc5c4STobin C. Harding '/sys/kernel/debug/tracing/trace_pipe', 41136fc5c4STobin C. Harding '/sys/kernel/security/apparmor/revision'); 42136fc5c4STobin C. Harding 43a284733eSTobin C. Harding# Do not parse these files under any subdirectory. 44136fc5c4STobin C. Hardingmy @skip_parse_files_any = ('0', 45136fc5c4STobin C. Harding '1', 46136fc5c4STobin C. Harding '2', 47136fc5c4STobin C. Harding 'pagemap', 48136fc5c4STobin C. Harding 'events', 49136fc5c4STobin C. Harding 'access', 50136fc5c4STobin C. Harding 'registers', 51136fc5c4STobin C. Harding 'snapshot_raw', 52136fc5c4STobin C. Harding 'trace_pipe_raw', 53136fc5c4STobin C. Harding 'ptmx', 54136fc5c4STobin C. Harding 'trace_pipe'); 55136fc5c4STobin C. Harding 56136fc5c4STobin C. Harding# Do not walk these directories (absolute path). 57136fc5c4STobin C. Hardingmy @skip_walk_dirs_abs = (); 58136fc5c4STobin C. Harding 59136fc5c4STobin C. Harding# Do not walk these directories under any subdirectory. 60136fc5c4STobin C. Hardingmy @skip_walk_dirs_any = ('self', 61136fc5c4STobin C. Harding 'thread-self', 62136fc5c4STobin C. Harding 'cwd', 63136fc5c4STobin C. Harding 'fd', 64136fc5c4STobin C. Harding 'stderr', 65136fc5c4STobin C. Harding 'stdin', 66136fc5c4STobin C. Harding 'stdout'); 67136fc5c4STobin C. Harding 68136fc5c4STobin C. Hardingsub help 69136fc5c4STobin C. Harding{ 70136fc5c4STobin C. Harding my ($exitcode) = @_; 71136fc5c4STobin C. Harding 72136fc5c4STobin C. Harding print << "EOM"; 73136fc5c4STobin C. HardingUsage: $P [OPTIONS] 74136fc5c4STobin C. HardingVersion: $V 75136fc5c4STobin C. Harding 76136fc5c4STobin C. HardingOptions: 77136fc5c4STobin C. Harding 78136fc5c4STobin C. Harding -d, --debug Display debugging output. 79136fc5c4STobin C. Harding -h, --help, --version Display this help and exit. 80136fc5c4STobin C. Harding 81136fc5c4STobin C. HardingScans the running (64 bit) kernel for potential leaking addresses. 82136fc5c4STobin C. Harding 83136fc5c4STobin C. HardingEOM 84136fc5c4STobin C. Harding exit($exitcode); 85136fc5c4STobin C. Harding} 86136fc5c4STobin C. Harding 87136fc5c4STobin C. HardingGetOptions( 88136fc5c4STobin C. Harding 'd|debug' => \$debug, 89136fc5c4STobin C. Harding 'h|help' => \$help, 90136fc5c4STobin C. Harding 'version' => \$help 91136fc5c4STobin C. Harding) or help(1); 92136fc5c4STobin C. Harding 93136fc5c4STobin C. Hardinghelp(0) if ($help); 94136fc5c4STobin C. Harding 95136fc5c4STobin C. Hardingparse_dmesg(); 96136fc5c4STobin C. Hardingwalk(@DIRS); 97136fc5c4STobin C. Harding 98136fc5c4STobin C. Hardingexit 0; 99136fc5c4STobin C. Harding 100136fc5c4STobin C. Hardingsub dprint 101136fc5c4STobin C. Harding{ 102136fc5c4STobin C. Harding printf(STDERR @_) if $debug; 103136fc5c4STobin C. Harding} 104136fc5c4STobin C. Harding 105136fc5c4STobin C. Hardingsub is_false_positive 106136fc5c4STobin C. Harding{ 107136fc5c4STobin C. Harding my ($match) = @_; 108136fc5c4STobin C. Harding 109136fc5c4STobin C. Harding if ($match =~ '\b(0x)?(f|F){16}\b' or 110136fc5c4STobin C. Harding $match =~ '\b(0x)?0{16}\b') { 111136fc5c4STobin C. Harding return 1; 112136fc5c4STobin C. Harding } 113136fc5c4STobin C. Harding 1147e5758f7STobin C. Harding 1157e5758f7STobin C. Harding if ($match =~ '\bf{10}600000\b' or# vsyscall memory region, we should probably check against a range here. 116136fc5c4STobin C. Harding $match =~ '\bf{10}601000\b') { 117136fc5c4STobin C. Harding return 1; 118136fc5c4STobin C. Harding } 119136fc5c4STobin C. Harding 120136fc5c4STobin C. Harding return 0; 121136fc5c4STobin C. Harding} 122136fc5c4STobin C. Harding 123136fc5c4STobin C. Harding# True if argument potentially contains a kernel address. 124136fc5c4STobin C. Hardingsub may_leak_address 125136fc5c4STobin C. Harding{ 126136fc5c4STobin C. Harding my ($line) = @_; 127136fc5c4STobin C. Harding my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b'; 128136fc5c4STobin C. Harding 129136fc5c4STobin C. Harding # Signal masks. 130136fc5c4STobin C. Harding if ($line =~ '^SigBlk:' or 131136fc5c4STobin C. Harding $line =~ '^SigCgt:') { 132136fc5c4STobin C. Harding return 0; 133136fc5c4STobin C. Harding } 134136fc5c4STobin C. Harding 135136fc5c4STobin C. Harding if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or 136136fc5c4STobin C. Harding $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') { 137136fc5c4STobin C. Harding return 0; 138136fc5c4STobin C. Harding } 139136fc5c4STobin C. Harding 140136fc5c4STobin C. Harding while (/($address)/g) { 141136fc5c4STobin C. Harding if (!is_false_positive($1)) { 142136fc5c4STobin C. Harding return 1; 143136fc5c4STobin C. Harding } 144136fc5c4STobin C. Harding } 145136fc5c4STobin C. Harding 146136fc5c4STobin C. Harding return 0; 147136fc5c4STobin C. Harding} 148136fc5c4STobin C. Harding 149136fc5c4STobin C. Hardingsub parse_dmesg 150136fc5c4STobin C. Harding{ 151136fc5c4STobin C. Harding open my $cmd, '-|', 'dmesg'; 152136fc5c4STobin C. Harding while (<$cmd>) { 153136fc5c4STobin C. Harding if (may_leak_address($_)) { 154136fc5c4STobin C. Harding print 'dmesg: ' . $_; 155136fc5c4STobin C. Harding } 156136fc5c4STobin C. Harding } 157136fc5c4STobin C. Harding close $cmd; 158136fc5c4STobin C. Harding} 159136fc5c4STobin C. Harding 160136fc5c4STobin C. Harding# True if we should skip this path. 161136fc5c4STobin C. Hardingsub skip 162136fc5c4STobin C. Harding{ 163136fc5c4STobin C. Harding my ($path, $paths_abs, $paths_any) = @_; 164136fc5c4STobin C. Harding 165136fc5c4STobin C. Harding foreach (@$paths_abs) { 166136fc5c4STobin C. Harding return 1 if (/^$path$/); 167136fc5c4STobin C. Harding } 168136fc5c4STobin C. Harding 169136fc5c4STobin C. Harding my($filename, $dirs, $suffix) = fileparse($path); 170136fc5c4STobin C. Harding foreach (@$paths_any) { 171136fc5c4STobin C. Harding return 1 if (/^$filename$/); 172136fc5c4STobin C. Harding } 173136fc5c4STobin C. Harding 174136fc5c4STobin C. Harding return 0; 175136fc5c4STobin C. Harding} 176136fc5c4STobin C. Harding 177136fc5c4STobin C. Hardingsub skip_parse 178136fc5c4STobin C. Harding{ 179136fc5c4STobin C. Harding my ($path) = @_; 180136fc5c4STobin C. Harding return skip($path, \@skip_parse_files_abs, \@skip_parse_files_any); 181136fc5c4STobin C. Harding} 182136fc5c4STobin C. Harding 183136fc5c4STobin C. Hardingsub parse_file 184136fc5c4STobin C. Harding{ 185136fc5c4STobin C. Harding my ($file) = @_; 186136fc5c4STobin C. Harding 187136fc5c4STobin C. Harding if (! -R $file) { 188136fc5c4STobin C. Harding return; 189136fc5c4STobin C. Harding } 190136fc5c4STobin C. Harding 191136fc5c4STobin C. Harding if (skip_parse($file)) { 192136fc5c4STobin C. Harding dprint "skipping file: $file\n"; 193136fc5c4STobin C. Harding return; 194136fc5c4STobin C. Harding } 195136fc5c4STobin C. Harding dprint "parsing: $file\n"; 196136fc5c4STobin C. Harding 197136fc5c4STobin C. Harding open my $fh, "<", $file or return; 198136fc5c4STobin C. Harding while ( <$fh> ) { 199136fc5c4STobin C. Harding if (may_leak_address($_)) { 200136fc5c4STobin C. Harding print $file . ': ' . $_; 201136fc5c4STobin C. Harding } 202136fc5c4STobin C. Harding } 203136fc5c4STobin C. Harding close $fh; 204136fc5c4STobin C. Harding} 205136fc5c4STobin C. Harding 206136fc5c4STobin C. Harding 207136fc5c4STobin C. Harding# True if we should skip walking this directory. 208136fc5c4STobin C. Hardingsub skip_walk 209136fc5c4STobin C. Harding{ 210136fc5c4STobin C. Harding my ($path) = @_; 211136fc5c4STobin C. Harding return skip($path, \@skip_walk_dirs_abs, \@skip_walk_dirs_any) 212136fc5c4STobin C. Harding} 213136fc5c4STobin C. Harding 214136fc5c4STobin C. Harding# Recursively walk directory tree. 215136fc5c4STobin C. Hardingsub walk 216136fc5c4STobin C. Harding{ 217136fc5c4STobin C. Harding my @dirs = @_; 218136fc5c4STobin C. Harding 219136fc5c4STobin C. Harding while (my $pwd = shift @dirs) { 220136fc5c4STobin C. Harding next if (skip_walk($pwd)); 221136fc5c4STobin C. Harding next if (!opendir(DIR, $pwd)); 222136fc5c4STobin C. Harding my @files = readdir(DIR); 223136fc5c4STobin C. Harding closedir(DIR); 224136fc5c4STobin C. Harding 225136fc5c4STobin C. Harding foreach my $file (@files) { 226136fc5c4STobin C. Harding next if ($file eq '.' or $file eq '..'); 227136fc5c4STobin C. Harding 228136fc5c4STobin C. Harding my $path = "$pwd/$file"; 229136fc5c4STobin C. Harding next if (-l $path); 230136fc5c4STobin C. Harding 231136fc5c4STobin C. Harding if (-d $path) { 232136fc5c4STobin C. Harding push @dirs, $path; 233136fc5c4STobin C. Harding } else { 234136fc5c4STobin C. Harding parse_file($path); 235136fc5c4STobin C. Harding } 236136fc5c4STobin C. Harding } 237136fc5c4STobin C. Harding } 238136fc5c4STobin C. Harding} 239