[RAMEN9610-21380]HID: hiddev: do cleanup in failure of opening a device
[GitHub/MotorolaMobilityLLC/kernel-slsi.git] / scripts / leaking_addresses.pl
CommitLineData
136fc5c4
TH
1#!/usr/bin/env perl
2#
3# (c) 2017 Tobin C. Harding <me@tobin.cc>
4# Licensed under the terms of the GNU GPL License version 2
5#
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).
9#
10# You can configure the behaviour of the script;
11#
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
15#
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
19#
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
23#
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.
28#
29# Use --debug to output path before parsing, this is useful to find files that
30# cause the script to choke.
31#
32# You may like to set kptr_restrict=2 before running script
33# (see Documentation/sysctl/kernel.txt).
34
35use warnings;
36use strict;
37use POSIX;
38use File::Basename;
39use File::Spec;
40use Cwd 'abs_path';
41use Term::ANSIColor qw(:constants);
42use Getopt::Long qw(:config no_auto_abbrev);
43
44my $P = $0;
45my $V = '0.01';
46
47# Directories to scan.
48my @DIRS = ('/proc', '/sys');
49
50# Command line options.
51my $help = 0;
52my $debug = 0;
53my @dont_walk = ();
54my @dont_parse = ();
55
56# Do not parse these files (absolute path).
57my @skip_parse_files_abs = ('/proc/kmsg',
58 '/proc/kcore',
59 '/proc/fs/ext4/sdb1/mb_groups',
60 '/proc/1/fd/3',
61 '/sys/kernel/debug/tracing/trace_pipe',
62 '/sys/kernel/security/apparmor/revision');
63
64# Do not parse thes files under any subdirectory.
65my @skip_parse_files_any = ('0',
66 '1',
67 '2',
68 'pagemap',
69 'events',
70 'access',
71 'registers',
72 'snapshot_raw',
73 'trace_pipe_raw',
74 'ptmx',
75 'trace_pipe');
76
77# Do not walk these directories (absolute path).
78my @skip_walk_dirs_abs = ();
79
80# Do not walk these directories under any subdirectory.
81my @skip_walk_dirs_any = ('self',
82 'thread-self',
83 'cwd',
84 'fd',
85 'stderr',
86 'stdin',
87 'stdout');
88
89sub help
90{
91 my ($exitcode) = @_;
92
93 print << "EOM";
94Usage: $P [OPTIONS]
95Version: $V
96
97Options:
98
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.
103
104If an absolute path is passed to --dont_XXX then this path is skipped. If a
105single filename is passed then this file/directory will be skipped when
106appearing under any subdirectory.
107
108Example:
109
110 # Just scan dmesg output.
111 scripts/leaking_addresses.pl --dont_walk_abs /proc --dont_walk_abs /sys
112
113Scans the running (64 bit) kernel for potential leaking addresses.
114
115EOM
116 exit($exitcode);
117}
118
119GetOptions(
120 'dont-walk=s' => \@dont_walk,
121 'dont-parse=s' => \@dont_parse,
122 'd|debug' => \$debug,
123 'h|help' => \$help,
124 'version' => \$help
125) or help(1);
126
127help(0) if ($help);
128
129push_to_global();
130
131parse_dmesg();
132walk(@DIRS);
133
134exit 0;
135
136sub debug_arrays
137{
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";
142}
143
144sub dprint
145{
146 printf(STDERR @_) if $debug;
147}
148
149sub push_in_abs_any
150{
151 my ($in, $abs, $any) = @_;
152
153 foreach my $path (@$in) {
154 if (File::Spec->file_name_is_absolute($path)) {
155 push @$abs, $path;
156 } elsif (index($path,'/') == -1) {
157 push @$any, $path;
158 } else {
159 print 'path error: ' . $path;
160 }
161 }
162}
163
164# Push command line options to global arrays.
165sub push_to_global
166{
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);
169}
170
171sub is_false_positive
172{
173 my ($match) = @_;
174
175 if ($match =~ '\b(0x)?(f|F){16}\b' or
176 $match =~ '\b(0x)?0{16}\b') {
177 return 1;
178 }
179
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') {
183 return 1;
184 }
185
186 return 0;
187}
188
189# True if argument potentially contains a kernel address.
190sub may_leak_address
191{
192 my ($line) = @_;
193 my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b';
194
195 # Signal masks.
196 if ($line =~ '^SigBlk:' or
197 $line =~ '^SigCgt:') {
198 return 0;
199 }
200
201 if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
202 $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {
203 return 0;
204 }
205
206 while (/($address)/g) {
207 if (!is_false_positive($1)) {
208 return 1;
209 }
210 }
211
212 return 0;
213}
214
215sub parse_dmesg
216{
217 open my $cmd, '-|', 'dmesg';
218 while (<$cmd>) {
219 if (may_leak_address($_)) {
220 print 'dmesg: ' . $_;
221 }
222 }
223 close $cmd;
224}
225
226# True if we should skip this path.
227sub skip
228{
229 my ($path, $paths_abs, $paths_any) = @_;
230
231 foreach (@$paths_abs) {
232 return 1 if (/^$path$/);
233 }
234
235 my($filename, $dirs, $suffix) = fileparse($path);
236 foreach (@$paths_any) {
237 return 1 if (/^$filename$/);
238 }
239
240 return 0;
241}
242
243sub skip_parse
244{
245 my ($path) = @_;
246 return skip($path, \@skip_parse_files_abs, \@skip_parse_files_any);
247}
248
249sub parse_file
250{
251 my ($file) = @_;
252
253 if (! -R $file) {
254 return;
255 }
256
257 if (skip_parse($file)) {
258 dprint "skipping file: $file\n";
259 return;
260 }
261 dprint "parsing: $file\n";
262
263 open my $fh, "<", $file or return;
264 while ( <$fh> ) {
265 if (may_leak_address($_)) {
266 print $file . ': ' . $_;
267 }
268 }
269 close $fh;
270}
271
272
273# True if we should skip walking this directory.
274sub skip_walk
275{
276 my ($path) = @_;
277 return skip($path, \@skip_walk_dirs_abs, \@skip_walk_dirs_any)
278}
279
280# Recursively walk directory tree.
281sub walk
282{
283 my @dirs = @_;
284 my %seen;
285
286 while (my $pwd = shift @dirs) {
287 next if (skip_walk($pwd));
288 next if (!opendir(DIR, $pwd));
289 my @files = readdir(DIR);
290 closedir(DIR);
291
292 foreach my $file (@files) {
293 next if ($file eq '.' or $file eq '..');
294
295 my $path = "$pwd/$file";
296 next if (-l $path);
297
298 if (-d $path) {
299 push @dirs, $path;
300 } else {
301 parse_file($path);
302 }
303 }
304 }
305}