summaryrefslogtreecommitdiffstats
path: root/scripts/leaking_addresses.pl
diff options
context:
space:
mode:
authorTobin C. Harding <me@tobin.cc>2017-11-08 23:07:15 -0500
committerTobin C. Harding <me@tobin.cc>2017-11-13 17:29:27 -0500
commitd09bd8da8812a4df69ea3303e6df846a729ec623 (patch)
treed04e30ca13d96137f5f8671f6deee855a673744d /scripts/leaking_addresses.pl
parent1c1e3be0bf37db1396b4ecd995992643a6d92c00 (diff)
leaking_addresses: add summary reporting options
Currently script just dumps all results found. Potentially, this risks losing single results among multiple duplicate results. We need some way of restricting duplicates to assist users of the script. It would also be nice if we got a report instead of raw results. Duplicates can be defined in various ways, instead of trying to find a single perfect solution we can present the user with various options to display the output. Doing so will typically lead to users wanting to view the output multiple times. Currently we scan the kernel each time, this is slow and unnecessary. We can expedite the process by writing the results to file for subsequent viewing. Add command line options to enable summary reporting, including options to write to and read from file. Signed-off-by: Tobin C. Harding <me@tobin.cc>
Diffstat (limited to 'scripts/leaking_addresses.pl')
-rwxr-xr-xscripts/leaking_addresses.pl191
1 files changed, 188 insertions, 3 deletions
diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl
index 0aac03a020a8..4610ad3c80c2 100755
--- a/scripts/leaking_addresses.pl
+++ b/scripts/leaking_addresses.pl
@@ -31,6 +31,13 @@ my @DIRS = ('/proc', '/sys');
31# Command line options. 31# Command line options.
32my $help = 0; 32my $help = 0;
33my $debug = 0; 33my $debug = 0;
34my $raw = 0;
35my $output_raw = ""; # Write raw results to file.
36my $input_raw = ""; # Read raw results from file instead of scanning.
37
38my $suppress_dmesg = 0; # Don't show dmesg in output.
39my $squash_by_path = 0; # Summary report grouped by absolute path.
40my $squash_by_filename = 0; # Summary report grouped by filename.
34 41
35# Do not parse these files (absolute path). 42# Do not parse these files (absolute path).
36my @skip_parse_files_abs = ('/proc/kmsg', 43my @skip_parse_files_abs = ('/proc/kmsg',
@@ -73,13 +80,31 @@ sub help
73 my ($exitcode) = @_; 80 my ($exitcode) = @_;
74 81
75 print << "EOM"; 82 print << "EOM";
83
76Usage: $P [OPTIONS] 84Usage: $P [OPTIONS]
77Version: $V 85Version: $V
78 86
79Options: 87Options:
80 88
81 -d, --debug Display debugging output. 89 -o, --output-raw=<file> Save results for future processing.
82 -h, --help, --version Display this help and exit. 90 -i, --input-raw=<file> Read results from file instead of scanning.
91 --raw Show raw results (default).
92 --suppress-dmesg Do not show dmesg results.
93 --squash-by-path Show one result per unique path.
94 --squash-by-filename Show one result per unique filename.
95 -d, --debug Display debugging output.
96 -h, --help, --version Display this help and exit.
97
98Examples:
99
100 # Scan kernel and dump raw results.
101 $0
102
103 # Scan kernel and save results to file.
104 $0 --output-raw scan.out
105
106 # View summary report.
107 $0 --input-raw scan.out --squash-by-filename
83 108
84Scans the running (64 bit) kernel for potential leaking addresses. 109Scans the running (64 bit) kernel for potential leaking addresses.
85 110
@@ -90,11 +115,33 @@ EOM
90GetOptions( 115GetOptions(
91 'd|debug' => \$debug, 116 'd|debug' => \$debug,
92 'h|help' => \$help, 117 'h|help' => \$help,
93 'version' => \$help 118 'version' => \$help,
119 'o|output-raw=s' => \$output_raw,
120 'i|input-raw=s' => \$input_raw,
121 'suppress-dmesg' => \$suppress_dmesg,
122 'squash-by-path' => \$squash_by_path,
123 'squash-by-filename' => \$squash_by_filename,
124 'raw' => \$raw,
94) or help(1); 125) or help(1);
95 126
96help(0) if ($help); 127help(0) if ($help);
97 128
129if ($input_raw) {
130 format_output($input_raw);
131 exit(0);
132}
133
134if (!$input_raw and ($squash_by_path or $squash_by_filename)) {
135 printf "\nSummary reporting only available with --input-raw=<file>\n";
136 printf "(First run scan with --output-raw=<file>.)\n";
137 exit(128);
138}
139
140if ($output_raw) {
141 open my $fh, '>', $output_raw or die "$0: $output_raw: $!\n";
142 select $fh;
143}
144
98parse_dmesg(); 145parse_dmesg();
99walk(@DIRS); 146walk(@DIRS);
100 147
@@ -239,3 +286,141 @@ sub walk
239 } 286 }
240 } 287 }
241} 288}
289
290sub format_output
291{
292 my ($file) = @_;
293
294 # Default is to show raw results.
295 if ($raw or (!$squash_by_path and !$squash_by_filename)) {
296 dump_raw_output($file);
297 return;
298 }
299
300 my ($total, $dmesg, $paths, $files) = parse_raw_file($file);
301
302 printf "\nTotal number of results from scan (incl dmesg): %d\n", $total;
303
304 if (!$suppress_dmesg) {
305 print_dmesg($dmesg);
306 }
307
308 if ($squash_by_filename) {
309 squash_by($files, 'filename');
310 }
311
312 if ($squash_by_path) {
313 squash_by($paths, 'path');
314 }
315}
316
317sub dump_raw_output
318{
319 my ($file) = @_;
320
321 open (my $fh, '<', $file) or die "$0: $file: $!\n";
322 while (<$fh>) {
323 if ($suppress_dmesg) {
324 if ("dmesg:" eq substr($_, 0, 6)) {
325 next;
326 }
327 }
328 print $_;
329 }
330 close $fh;
331}
332
333sub parse_raw_file
334{
335 my ($file) = @_;
336
337 my $total = 0; # Total number of lines parsed.
338 my @dmesg; # dmesg output.
339 my %files; # Unique filenames containing leaks.
340 my %paths; # Unique paths containing leaks.
341
342 open (my $fh, '<', $file) or die "$0: $file: $!\n";
343 while (my $line = <$fh>) {
344 $total++;
345
346 if ("dmesg:" eq substr($line, 0, 6)) {
347 push @dmesg, $line;
348 next;
349 }
350
351 cache_path(\%paths, $line);
352 cache_filename(\%files, $line);
353 }
354
355 return $total, \@dmesg, \%paths, \%files;
356}
357
358sub print_dmesg
359{
360 my ($dmesg) = @_;
361
362 print "\ndmesg output:\n";
363
364 if (@$dmesg == 0) {
365 print "<no results>\n";
366 return;
367 }
368
369 foreach(@$dmesg) {
370 my $index = index($_, ': ');
371 $index += 2; # skid ': '
372 print substr($_, $index);
373 }
374}
375
376sub squash_by
377{
378 my ($ref, $desc) = @_;
379
380 print "\nResults squashed by $desc (excl dmesg). ";
381 print "Displaying [<number of results> <$desc>], <example result>\n";
382
383 if (keys %$ref == 0) {
384 print "<no results>\n";
385 return;
386 }
387
388 foreach(keys %$ref) {
389 my $lines = $ref->{$_};
390 my $length = @$lines;
391 printf "[%d %s] %s", $length, $_, @$lines[0];
392 }
393}
394
395sub cache_path
396{
397 my ($paths, $line) = @_;
398
399 my $index = index($line, ': ');
400 my $path = substr($line, 0, $index);
401
402 $index += 2; # skip ': '
403 add_to_cache($paths, $path, substr($line, $index));
404}
405
406sub cache_filename
407{
408 my ($files, $line) = @_;
409
410 my $index = index($line, ': ');
411 my $path = substr($line, 0, $index);
412 my $filename = basename($path);
413
414 $index += 2; # skip ': '
415 add_to_cache($files, $filename, substr($line, $index));
416}
417
418sub add_to_cache
419{
420 my ($cache, $key, $value) = @_;
421
422 if (!$cache->{$key}) {
423 $cache->{$key} = ();
424 }
425 push @{$cache->{$key}}, $value;
426}