Skip to contentSkip to conversation

scripts: leaking_addresses.pl: add support for 32-bit kernel addresses

PATCH In review [email protected] 2 participants 3 messages Nov 21, 2017 – Nov 22, 2017 LEAKING_ADDRESSES

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 scripts: leaking_addresses.pl: add support for 32-bit kernel addresses

The current leaking_addresses.pl script only supports showing "leaked" 64-bit kernel virtual addresses. This patch adds support for showing "leaked" 32-bit kernel virtual addresses.

The way it currently works- once it detects we're running on an i'x'86 platform (where x=3|4|5|6), it takes this arch into account for checking: the essential rationale:
virt-addr >= PAGE_OFFSET => it's a kernel virtual address.

Note-
1. It's a work in progress; some pending TODOs:
- support for ARM-32
- programatically query and set the PAGE_OFFSET based on arch (it's currently hard-coded)

2. Minor edit:
the '--raw', '--suppress-dmesg', '--squash-by-path' and '--squash-by-filename' option switches are only meaningful
when the '----input-raw=' option is used. So, indent the 'Help' screen lines to reflect the fact.

Feedback welcome..

---

scripts/leaking_addresses.pl +37 -7
index bc5788000018..e139de445ad1 100755
@@ -12,7 +12,10 @@
 #
 # You may like to set kptr_restrict=2 before running script
 # (see Documentation/sysctl/kernel.txt).
-
+#
+# 32-bit kernel address support : Kaiwan N Billimoria
+#                       <[email protected]>
+#
 use warnings;
 use strict;
 use POSIX;
@@ -35,7 +38,7 @@ my $TIMEOUT = 10;
 # Script can only grep for kernel addresses on the following architectures. If
 # your architecture is not listed here and has a grep'able kernel address please
 # consider submitting a patch.
-my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64');
+my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64', 'i[3456]86');
 
 # Command line options.
 my $help = 0;
@@ -48,6 +51,9 @@ 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 $bit_size = 64;   # Check 64-bit kernel addresses by default
+my $PAGE_OFFSET_32BIT = 0xc0000000;
+
 # Do not parse these files (absolute path).
 my @skip_parse_files_abs = ('/proc/kmsg',
 			    '/proc/kcore',
@@ -97,10 +103,10 @@ Options:
 
 	-o, --output-raw=<file>  Save results for future processing.
 	-i, --input-raw=<file>   Read results from file instead of scanning.
-	    --raw                Show raw results (default).
-	    --suppress-dmesg     Do not show dmesg results.
-	    --squash-by-path     Show one result per unique path.
-	    --squash-by-filename Show one result per unique filename.
+	      --raw                 Show raw results (default).
+	      --suppress-dmesg      Do not show dmesg results.
+	      --squash-by-path      Show one result per unique path.
+	      --squash-by-filename  Show one result per unique filename.
 	-d, --debug              Display debugging output.
 	-h, --help, --version    Display this help and exit.
 
@@ -177,7 +183,7 @@ sub dprint
 
 sub is_supported_architecture
 {
-	return (is_x86_64() or is_ppc64());
+	return (is_x86_64() or is_ppc64() or is_ix86_32());
 }
 
 sub is_x86_64
@@ -185,6 +191,7 @@ sub is_x86_64
 	my $archname = $Config{archname};
 
 	if ($archname =~ m/x86_64/) {
+		$bit_size=64;
 		return 1;
 	}
 	return 0;
@@ -195,6 +202,19 @@ sub is_ppc64
 	my $archname = $Config{archname};
 
 	if ($archname =~ m/powerpc/ and $archname =~ m/64/) {
+		$bit_size=64;
+		return 1;
+	}
+	return 0;
+}
+
+# 32-bit x86: is_i'x'86_32() ; where x is [3 or 4 or 5 or 6]
+sub is_ix86_32
+{
+	my $archname = $Config{archname};
+
+	if ($archname =~ m/i[3456]86-linux/) {
+		$bit_size=32;
 		return 1;
 	}
 	return 0;
@@ -215,6 +235,15 @@ sub is_false_positive
 		    $match =~ '\bf{10}601000\b') {
 			return 1;
 		}
+	} elsif ($bit_size == 32) {
+		my $addr32 = eval hex($match);
+		if ($addr32 < $PAGE_OFFSET_32BIT ) {
+			return 1;
+		}
+		if ($match =~ '\b(0x)?(f|F){8}\b') {
+			return 1;
+		}
 	}
 
 	return 0;
@@ -243,6 +272,8 @@ sub may_leak_address
 		$address_re = '\b(0x)?ffff[[:xdigit:]]{12}\b';
 	} elsif (is_ppc64()) {
 		$address_re = '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b';
+	} elsif (is_ix86_32()) {
+		$address_re = '\b(0x)?[[:xdigit:]]{8}\b';
 	}
 
 	while (/($address_re)/g) {
#1 lore raw

You don't typically need [xxx v1] for version 1, the v1 is implicit.

Please use the git brief description prefix that is already in use i.e

leaking_addresses: add support for 32-bit kernel addresses

Quoted 3 lines · On Tue, Nov 21, 2017 at 01:28:14PM +0530, [email protected] wrote:

The current leaking_addresses.pl script only supports showing "leaked" 64-bit kernel virtual addresses. This patch adds support for showing "leaked" 32-bit kernel virtual addresses.

Thanks for the patch. I like your ideas. Here are a few nitpicks to help us get your work merged.

Quoted 7 lines

The way it currently works- once it detects we're running on an i'x'86 platform (where x=3|4|5|6), it takes this arch into account for checking: the essential rationale:
virt-addr >= PAGE_OFFSET => it's a kernel virtual address.

Note-
1. It's a work in progress; some pending TODOs:

We don't have 'work in progress' in the kernel, everything is a work in progress. We just have RFC or PATCH.

Quoted 1 line

- support for ARM-32

Sure, we can do this later.

Quoted 2 lines

- programatically query and set the PAGE_OFFSET based on arch (it's currently hard-coded)

Let's do this straight away, it will be much nicer.

Quoted 5 lines

2. Minor edit:
the '--raw', '--suppress-dmesg', '--squash-by-path' and '--squash-by-filename' option switches are only meaningful
when the '----input-raw=' option is used. So, indent the 'Help' screen lines to reflect the fact.

This is a different change to the architecture stuff so should be in a separate patch. You could do both as a series if you like. Off the top of my head I have never seen options output like this, but if you have, I'm willing to accept your view. You are correct that the options mentioned are only use in conjuncture with '--input-raw' so some way of showing this would be nice.

Quoted 1 line

Feedback welcome..

Here it comes ;)

Quoted 7 lines

---

scripts/leaking_addresses.pl +0 -0
index bc5788000018..e139de445ad1 100755

scripts/leaking_addresses.pl line 17

Quoted 7 lines
scripts/leaking_addresses.pl +3 -1
@@ -12,7 +12,10 @@
 #
 # You may like to set kptr_restrict=2 before running script
 # (see Documentation/sysctl/kernel.txt).
-
+#
+# 32-bit kernel address support : Kaiwan N Billimoria
+#                       <[email protected]>

Cool. Can you put this up the top near the other copyright information please.

Perhaps

# (c) 2017 Kaiwan N Billimoria <[email protected]> (ix86 support)

Quoted 13 lines
scripts/leaking_addresses.pl +1 -1
 use warnings;
 use strict;
 use POSIX;
@@ -35,7 +38,7 @@ my $TIMEOUT = 10;
 # Script can only grep for kernel addresses on the following architectures. If
 # your architecture is not listed here and has a grep'able kernel address please
 # consider submitting a patch.
-my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64');
+my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64', 'i[3456]86');
 
 # Command line options.
 my $help = 0;

scripts/leaking_addresses.pl my $suppress_dmesg = 0; # Don't show dmesg in output. · line 54

Quoted 4 lines
scripts/leaking_addresses.pl +1 -0
@@ -48,6 +51,9 @@ 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 $bit_size = 64;   # Check 64-bit kernel addresses by default

This global is unnecessary. You already have is_ix86_32() so you can just use that.

scripts/leaking_addresses.pl my $suppress_dmesg = 0; # Don't show dmesg in output. · line 55

Quoted 1 line
scripts/leaking_addresses.pl +1 -0
+my $PAGE_OFFSET_32BIT = 0xc0000000;

As commented above, can we do this programmatically like previously discussed?

Quoted 7 lines
scripts/leaking_addresses.pl +0 -0
 # Do not parse these files (absolute path).
 my @skip_parse_files_abs = ('/proc/kmsg',
 			    '/proc/kcore',
@@ -97,10 +103,10 @@ Options:
 
 	-o, --output-raw=<file>  Save results for future processing.
 	-i, --input-raw=<file>   Read results from file instead of scanning.

scripts/leaking_addresses.pl Options: · line 111

Quoted 10 lines
scripts/leaking_addresses.pl +4 -4
-	    --raw                Show raw results (default).
-	    --suppress-dmesg     Do not show dmesg results.
-	    --squash-by-path     Show one result per unique path.
-	    --squash-by-filename Show one result per unique filename.
+	      --raw                 Show raw results (default).
+	      --suppress-dmesg      Do not show dmesg results.
+	      --squash-by-path      Show one result per unique path.
+	      --squash-by-filename  Show one result per unique filename.
 	-d, --debug              Display debugging output.
 	-h, --help, --version    Display this help and exit.

Commented on above.

Quoted 10 lines
scripts/leaking_addresses.pl +1 -1
@@ -177,7 +183,7 @@ sub dprint
 
 sub is_supported_architecture
 {
-	return (is_x86_64() or is_ppc64());
+	return (is_x86_64() or is_ppc64() or is_ix86_32());
 }
 
 sub is_x86_64

scripts/leaking_addresses.pl sub is_x86_64 · line 194

Quoted 4 lines
scripts/leaking_addresses.pl +1 -0
@@ -185,6 +191,7 @@ sub is_x86_64
 	my $archname = $Config{archname};
 
 	if ($archname =~ m/x86_64/) {
+		$bit_size=64;

Setting a global opaquely within an is_a_ function (or subroutine) violates the principle of least surprise. No one would expect an is_a function to do this. Either way, as commented above, we don't need this global anyways.

scripts/leaking_addresses.pl sub is_ppc64 · line 205

Quoted 8 lines
scripts/leaking_addresses.pl +1 -0
 		return 1;
 	}
 	return 0;
@@ -195,6 +202,19 @@ sub is_ppc64
 	my $archname = $Config{archname};
 
 	if ($archname =~ m/powerpc/ and $archname =~ m/64/) {
+		$bit_size=64;

As above.

scripts/leaking_addresses.pl sub is_ppc64 · line 211

Quoted 6 lines
scripts/leaking_addresses.pl +6 -0
+		return 1;
+	}
+	return 0;
+}
+
+# 32-bit x86: is_i'x'86_32() ; where x is [3 or 4 or 5 or 6]

Perhaps

# true for Intel x86 32 bit architectures, x386 and above.

scripts/leaking_addresses.pl sub is_ppc64 · line 217

Quoted 6 lines
scripts/leaking_addresses.pl +6 -0
+sub is_ix86_32
+{
+	my $archname = $Config{archname};
+
+	if ($archname =~ m/i[3456]86-linux/) {
+		$bit_size=32;

Please use kernel coding style

		$bit_size = 32;

scripts/leaking_addresses.pl sub is_false_positive · line 238

Quoted 8 lines
scripts/leaking_addresses.pl +1 -0
 		return 1;
 	}
 	return 0;
@@ -215,6 +235,15 @@ sub is_false_positive
 		    $match =~ '\bf{10}601000\b') {
 			return 1;
 		}
+	} elsif ($bit_size == 32) {
	} elsif (is_ix86_32())

Bonus points, you uncovered a bug in the current script if (is_x86_64) was missing the parenthesis!

# leaking_addresses.pl: Scan 64 bit kernel for potential leaking addresses. @@ -209,7 +211,7 @@ sub is_false_positive

                return 1;
        }

- if (is_x86_64) {
+ if (is_x86_64()) {

Thanks. I'm guessing this is what led you to using the global :) Have patched dev branch (branch name: leaks).

scripts/leaking_addresses.pl sub is_false_positive · line 242

Quoted 4 lines
scripts/leaking_addresses.pl +4 -0
+		my $addr32 = eval hex($match);
+		if ($addr32 < $PAGE_OFFSET_32BIT ) {
+			return 1;
+		}

Nice.

scripts/leaking_addresses.pl sub is_false_positive · line 245

Quoted 3 lines
scripts/leaking_addresses.pl +3 -0
+		if ($match =~ '\b(0x)?(f|F){8}\b') {
+			return 1;
+		}

So, may_leak_address() and is_false_positive() are tightly coupled and not really that nice. Once we add 32 bit support it gets worse. Going forwards, we can either add your 32 bit work then refactor both functions or you can refactor them as you add the 32 bit stuff. I'm open to either. Some things to note

- The mask stuff (all 1's) should have an all 0's regex also.
- The mask stuff should probably be closer to the mask stuff for 64 bit. It's not immediately apparent a clean way to do this though.
- It's not immediately apparent if an address less that PAGE_OFFSET is a false positive or should be caught in leaks_address().
- Do we need 32 bit equivalents for

	if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
	    $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {

Even though this is a false positive it needs to be where it is (note the 'tightly coupled' statement above).

scripts/leaking_addresses.pl sub may_leak_address · line 279

Quoted 12 lines
scripts/leaking_addresses.pl +2 -0
 	}
 
 	return 0;
@@ -243,6 +272,8 @@ sub may_leak_address
 		$address_re = '\b(0x)?ffff[[:xdigit:]]{12}\b';
 	} elsif (is_ppc64()) {
 		$address_re = '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b';
+	} elsif (is_ix86_32()) {
+		$address_re = '\b(0x)?[[:xdigit:]]{8}\b';
 	}
 
 	while (/($address_re)/g) {

Your patch did not apply, the problem looks to be in the code section above. You can see that there is no removed line. For next spin please check your patch applies on top of the 'leaks' branch (which now includes the fix for the bug you found).

I have one more comment that should have been at the top but I did not want to confuse things. Typically, the git brief description should be limited to 50 characters. If you do decide to split this patch into two and use the prefix suggested you may like to change the git brief description but don't feel you have to. If you do decide to do this, your next patch set will be a version 1 again. I may be wrong but I never increment a patch version if the subject line changes (excluding contents of [] ).

I'm relatively new here so please feel free to ignore anything I've said. I have commented in the effort to help you learn as I am doing so also.

I'll look forward to v2. Please ask if anything I've said is unclear.

thanks,
Tobin.

#2 lore raw

Thanks Tobin, for your detailed comments.

Quoted 5 lines · Tobin C. Harding

You don't typically need [xxx v1] for version 1, the v1 is implicit.

Please use the git brief description prefix that is already in use i.e

leaking_addresses: add support for 32-bit kernel addresses

Ok..

Quoted 4 lines · On Tue, Nov 21, 2017 at 01:28:14PM +0530, [email protected] wrote:
Quoted 1 line

- support for ARM-32

Sure, we can do this later.

Righto

Quoted 5 lines
Quoted 3 lines

- programatically query and set the PAGE_OFFSET based on arch (it's currently hard-coded)

Let's do this straight away, it will be much nicer.

Yes, will work on it..

Quoted 6 lines
Quoted 5 lines

2. Minor edit:
the '--raw', '--suppress-dmesg', '--squash-by-path' and '--squash-by-filename' option switches are only meaningful
when the '----input-raw=' option is used. So, indent the 'Help' screen lines to reflect the fact.

Quoted 6 lines

This is a different change to the architecture stuff so should be in a separate patch. You could do both as a series if you like. Off the top of my head I have never seen options output like this, but if you have, I'm willing to accept your view. You are correct that the options mentioned are only use in conjuncture with '--input-raw' so some way of showing this would be nice.

I realize this; so, yeah, will make the next one a series and put this in the 2nd..

Quoted 11 lines
Quoted 2 lines

+my $bit_size = 64; # Check 64-bit kernel addresses by default

This global is unnecessary. You already have is_ix86_32() so you can just use that.

From your later comments, I think you see that using this global is necessary.

Please use kernel coding style

                $bit_size = 32;

Ok..

Quoted 2 lines

Bonus points, you uncovered a bug in the current script if (is_x86_64) was missing the parenthesis!

Yeah :-)

Quoted 10 lines

scripts/leaking_addresses.pl sub is_false_positive · line 245

Quoted 4 lines
scripts/leaking_addresses.pl +3 -0
+             if ($match =~ '\b(0x)?(f|F){8}\b') {
+                     return 1;
+             }

So, may_leak_address() and is_false_positive() are tightly coupled and not really that nice. Once we add 32 bit support it gets worse. Going forwards, we can either add your 32 bit work then refactor both functions or you can refactor them as you add the 32 bit stuff. I'm open to either.

Yes I agree. Having said that, I'll leave it on the back burner for now..

Quoted 3 lines

Some things to note

- The mask stuff (all 1's) should have an all 0's regex also.

Well, once we determine the address is >= PAGE_OFFSET, it's automatically apparent that it's not 0, yes?

Quoted 4 lines

- The mask stuff should probably be closer to the mask stuff for 64 bit. It's not immediately apparent a clean way to do this though.
- It's not immediately apparent if an address less that PAGE_OFFSET is a false positive or should be caught in leaks_address().

Hmm only thing I can think of offhand- on many ARM-32's, the kernel module space is below
PAGE_OFFSET; we'd have to take that into consideration of course. Anything else < PAGE_OFFSET and a kernel address? Anyone?

Quoted 5 lines

- Do we need 32 bit equivalents for

        if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
            $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {

Ok am unclear on what exactly the above achieves.. could you pl throw some light on it, thanks..

Quoted 5 lines

Your patch did not apply, the problem looks to be in the code section above. You can see that there is no removed line. For next spin please check your patch applies on top of the 'leaks' branch (which now includes the fix for the bug you found).

Yes, sorry about that; will do..

Quoted 8 lines

I have one more comment that should have been at the top but I did not want to confuse things. Typically, the git brief description should be limited to 50 characters. If you do decide to split this patch into two and use the prefix suggested you may like to change the git brief description but don't feel you have to. If you do decide to do this, your next patch set will be a version 1 again. I may be wrong but I never increment a patch version if the subject line changes (excluding contents of [] ).

Right. I plan to send the next one as a 2 patch series; will keep the git prefix you suggest
(and as Sub changes, will not label the version).

Quoted 3 lines

thanks,
Tobin.

Thanks,
Kaiwan.