leaking_addresses: Provide mechanism to scan binary files

Introduce --kallsyms argument for scanning binary files for known symbol
addresses. This would have found the exposure in /sys/kernel/notes:

$ scripts/leaking_addresses.pl --kallsyms=<(sudo cat /proc/kallsyms)
/sys/kernel/notes: hypercall_page @ 156
/sys/kernel/notes: xen_hypercall_set_trap_table @ 156
/sys/kernel/notes: startup_xen @ 132

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Tycho Andersen <tandersen@netflix.com>
Link: https://lore.kernel.org/r/20240222220053.1475824-4-keescook@chromium.org
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Kees Cook 2024-02-22 14:00:51 -08:00
parent 3e389d457b
commit 67bbd2f007

View File

@ -52,10 +52,13 @@ my $input_raw = ""; # Read raw results from file instead of scanning.
my $suppress_dmesg = 0; # Don't show dmesg in output.
my $squash_by_path = 0; # Summary report grouped by absolute path.
my $squash_by_filename = 0; # Summary report grouped by filename.
my $kallsyms_file = ""; # Kernel symbols file.
my $kernel_config_file = ""; # Kernel configuration file.
my $opt_32bit = 0; # Scan 32-bit kernel.
my $page_offset_32bit = 0; # Page offset for 32-bit kernel.
my @kallsyms = ();
# Skip these absolute paths.
my @skip_abs = (
'/proc/kmsg',
@ -96,6 +99,8 @@ Options:
--squash-by-path Show one result per unique path.
--squash-by-filename Show one result per unique filename.
--kernel-config-file=<file> Kernel configuration file (e.g /boot/config)
--kallsyms=<file> Read kernel symbol addresses from file (for
scanning binary files).
--32-bit Scan 32-bit kernel.
--page-offset-32-bit=o Page offset (for 32-bit kernel 0xABCD1234).
-d, --debug Display debugging output.
@ -116,6 +121,7 @@ GetOptions(
'squash-by-path' => \$squash_by_path,
'squash-by-filename' => \$squash_by_filename,
'raw' => \$raw,
'kallsyms=s' => \$kallsyms_file,
'kernel-config-file=s' => \$kernel_config_file,
'32-bit' => \$opt_32bit,
'page-offset-32-bit=o' => \$page_offset_32bit,
@ -156,6 +162,25 @@ if ($output_raw) {
select $fh;
}
if ($kallsyms_file) {
open my $fh, '<', $kallsyms_file or die "$0: $kallsyms_file: $!\n";
while (<$fh>) {
chomp;
my @entry = split / /, $_;
my $addr_text = $entry[0];
if ($addr_text !~ /^0/) {
# TODO: Why is hex() so impossibly slow?
my $addr = hex($addr_text);
my $symbol = $entry[2];
# Only keep kernel text addresses.
my $long = pack("J", $addr);
my $entry = [$long, $symbol];
push @kallsyms, $entry;
}
}
close $fh;
}
parse_dmesg();
walk(@DIRS);
@ -447,6 +472,25 @@ sub timed_parse_file
}
}
sub parse_binary
{
my ($file) = @_;
open my $fh, "<:raw", $file or return;
local $/ = undef;
my $bytes = <$fh>;
close $fh;
foreach my $entry (@kallsyms) {
my $addr = $entry->[0];
my $symbol = $entry->[1];
my $offset = index($bytes, $addr);
if ($offset != -1) {
printf("$file: $symbol @ $offset\n");
}
}
}
sub parse_file
{
my ($file) = @_;
@ -456,6 +500,15 @@ sub parse_file
}
if (! -T $file) {
if ($file =~ m|^/sys/kernel/btf/| or
$file =~ m|^/sys/devices/pci| or
$file =~ m|^/sys/firmware/efi/efivars/| or
$file =~ m|^/proc/bus/pci/|) {
return;
}
if (scalar @kallsyms > 0) {
parse_binary($file);
}
return;
}