3 # (c) 2017 Tobin C. Harding <me@tobin.cc>
4 # Licensed under the terms of the GNU GPL License version 2
6 # leaking_addresses.pl: Scan 64 bit kernel for potential leaking addresses.
7 # - Scans dmesg output.
8 # - Walks directory tree and parses each file (for each directory in @DIRS).
10 # You can configure the behaviour of the script;
12 # - By adding paths, for directories you do not want to walk;
13 # absolute paths: @skip_walk_dirs_abs
14 # directory names: @skip_walk_dirs_any
16 # - By adding paths, for files you do not want to parse;
17 # absolute paths: @skip_parse_files_abs
18 # file names: @skip_parse_files_any
20 # The use of @skip_xxx_xxx_any causes files to be skipped where ever they occur.
21 # For example adding 'fd' to @skip_walk_dirs_any causes the fd/ directory to be
22 # skipped for all PID sub-directories of /proc
24 # The same thing can be achieved by passing command line options to --dont-walk
25 # and --dont-parse. If absolute paths are supplied to these options they are
26 # appended to the @skip_xxx_xxx_abs arrays. If file names are supplied to these
27 # options, they are appended to the @skip_xxx_xxx_any arrays.
29 # Use --debug to output path before parsing, this is useful to find files that
30 # cause the script to choke.
32 # You may like to set kptr_restrict=2 before running script
33 # (see Documentation/sysctl/kernel.txt).
41 use Term
::ANSIColor
qw(:constants);
42 use Getopt
::Long
qw(:config no_auto_abbrev);
47 # Directories to scan.
48 my @DIRS = ('/proc', '/sys');
50 # Command line options.
56 # Do not parse these files (absolute path).
57 my @skip_parse_files_abs = ('/proc/kmsg',
59 '/proc/fs/ext4/sdb1/mb_groups',
61 '/sys/kernel/debug/tracing/trace_pipe',
62 '/sys/kernel/security/apparmor/revision');
64 # Do not parse thes files under any subdirectory.
65 my @skip_parse_files_any = ('0',
77 # Do not walk these directories (absolute path).
78 my @skip_walk_dirs_abs = ();
80 # Do not walk these directories under any subdirectory.
81 my @skip_walk_dirs_any = ('self',
99 --dont
-walk
=<dir
> Don
't walk tree starting at <dir>.
100 --dont-parse=<file> Don't parse
<file
>.
101 -d
, --debug Display debugging output
.
102 -h
, --help
, --version Display this help
and exit.
104 If an absolute path is passed to
--dont_XXX then this path is skipped
. If a
105 single filename is passed then this file
/directory will be skipped
when
106 appearing under any subdirectory
.
110 # Just scan dmesg output.
111 scripts
/leaking_addresses
.pl
--dont_walk_abs
/proc
--dont_walk_abs
/sys
113 Scans the running
(64 bit
) kernel
for potential leaking addresses
.
120 'dont-walk=s' => \
@dont_walk,
121 'dont-parse=s' => \
@dont_parse,
122 'd|debug' => \
$debug,
138 print 'dirs_any: ' . join(", ", @skip_walk_dirs_any) . "\n";
139 print 'dirs_abs: ' . join(", ", @skip_walk_dirs_abs) . "\n";
140 print 'parse_any: ' . join(", ", @skip_parse_files_any) . "\n";
141 print 'parse_abs: ' . join(", ", @skip_parse_files_abs) . "\n";
146 printf(STDERR
@_) if $debug;
151 my ($in, $abs, $any) = @_;
153 foreach my $path (@
$in) {
154 if (File
::Spec
->file_name_is_absolute($path)) {
156 } elsif (index($path,'/') == -1) {
159 print 'path error: ' . $path;
164 # Push command line options to global arrays.
167 push_in_abs_any
(\
@dont_walk, \
@skip_walk_dirs_abs, \
@skip_walk_dirs_any);
168 push_in_abs_any
(\
@dont_parse, \
@skip_parse_files_abs, \
@skip_parse_files_any);
171 sub is_false_positive
175 if ($match =~ '\b(0x)?(f|F){16}\b' or
176 $match =~ '\b(0x)?0{16}\b') {
180 # vsyscall memory region, we should probably check against a range here.
181 if ($match =~ '\bf{10}600000\b' or
182 $match =~ '\bf{10}601000\b') {
189 # True if argument potentially contains a kernel address.
193 my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b';
196 if ($line =~ '^SigBlk:' or
197 $line =~ '^SigCgt:') {
201 if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
202 $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {
206 while (/($address)/g) {
207 if (!is_false_positive
($1)) {
217 open my $cmd, '-|', 'dmesg';
219 if (may_leak_address
($_)) {
220 print 'dmesg: ' . $_;
226 # True if we should skip this path.
229 my ($path, $paths_abs, $paths_any) = @_;
231 foreach (@
$paths_abs) {
232 return 1 if (/^$path$/);
235 my($filename, $dirs, $suffix) = fileparse
($path);
236 foreach (@
$paths_any) {
237 return 1 if (/^$filename$/);
246 return skip
($path, \
@skip_parse_files_abs, \
@skip_parse_files_any);
257 if (skip_parse
($file)) {
258 dprint
"skipping file: $file\n";
261 dprint
"parsing: $file\n";
263 open my $fh, "<", $file or return;
265 if (may_leak_address
($_)) {
266 print $file . ': ' . $_;
273 # True if we should skip walking this directory.
277 return skip
($path, \
@skip_walk_dirs_abs, \
@skip_walk_dirs_any)
280 # Recursively walk directory tree.
286 while (my $pwd = shift @dirs) {
287 next if (skip_walk
($pwd));
288 next if (!opendir(DIR
, $pwd));
289 my @files = readdir(DIR
);
292 foreach my $file (@files) {
293 next if ($file eq '.' or $file eq '..');
295 my $path = "$pwd/$file";