tty: Handle problem if line discipline does not have receive_buf
[linux/fpc-iii.git] / scripts / leaking_addresses.pl
blob2977371b29563bd2d5b9bc4917eee403f746ae5a
1 #!/usr/bin/env perl
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).
35 use warnings;
36 use strict;
37 use POSIX;
38 use File::Basename;
39 use File::Spec;
40 use Cwd 'abs_path';
41 use Term::ANSIColor qw(:constants);
42 use Getopt::Long qw(:config no_auto_abbrev);
44 my $P = $0;
45 my $V = '0.01';
47 # Directories to scan.
48 my @DIRS = ('/proc', '/sys');
50 # Command line options.
51 my $help = 0;
52 my $debug = 0;
53 my @dont_walk = ();
54 my @dont_parse = ();
56 # Do not parse these files (absolute path).
57 my @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');
64 # Do not parse thes files under any subdirectory.
65 my @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');
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',
82 'thread-self',
83 'cwd',
84 'fd',
85 'stderr',
86 'stdin',
87 'stdout');
89 sub help
91 my ($exitcode) = @_;
93 print << "EOM";
94 Usage: $P [OPTIONS]
95 Version: $V
97 Options:
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.
108 Example:
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.
116 exit($exitcode);
119 GetOptions(
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);
127 help(0) if ($help);
129 push_to_global();
131 parse_dmesg();
132 walk(@DIRS);
134 exit 0;
136 sub debug_arrays
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";
144 sub dprint
146 printf(STDERR @_) if $debug;
149 sub push_in_abs_any
151 my ($in, $abs, $any) = @_;
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;
164 # Push command line options to global arrays.
165 sub push_to_global
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
173 my ($match) = @_;
175 if ($match =~ '\b(0x)?(f|F){16}\b' or
176 $match =~ '\b(0x)?0{16}\b') {
177 return 1;
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;
186 return 0;
189 # True if argument potentially contains a kernel address.
190 sub may_leak_address
192 my ($line) = @_;
193 my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b';
195 # Signal masks.
196 if ($line =~ '^SigBlk:' or
197 $line =~ '^SigCgt:') {
198 return 0;
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;
206 while (/($address)/g) {
207 if (!is_false_positive($1)) {
208 return 1;
212 return 0;
215 sub parse_dmesg
217 open my $cmd, '-|', 'dmesg';
218 while (<$cmd>) {
219 if (may_leak_address($_)) {
220 print 'dmesg: ' . $_;
223 close $cmd;
226 # True if we should skip this path.
227 sub skip
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$/);
240 return 0;
243 sub skip_parse
245 my ($path) = @_;
246 return skip($path, \@skip_parse_files_abs, \@skip_parse_files_any);
249 sub parse_file
251 my ($file) = @_;
253 if (! -R $file) {
254 return;
257 if (skip_parse($file)) {
258 dprint "skipping file: $file\n";
259 return;
261 dprint "parsing: $file\n";
263 open my $fh, "<", $file or return;
264 while ( <$fh> ) {
265 if (may_leak_address($_)) {
266 print $file . ': ' . $_;
269 close $fh;
273 # True if we should skip walking this directory.
274 sub skip_walk
276 my ($path) = @_;
277 return skip($path, \@skip_walk_dirs_abs, \@skip_walk_dirs_any)
280 # Recursively walk directory tree.
281 sub walk
283 my @dirs = @_;
284 my %seen;
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);
292 foreach my $file (@files) {
293 next if ($file eq '.' or $file eq '..');
295 my $path = "$pwd/$file";
296 next if (-l $path);
298 if (-d $path) {
299 push @dirs, $path;
300 } else {
301 parse_file($path);