linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V9] printk: hash addresses printed with %p
@ 2017-10-29 22:59 Tobin C. Harding
  2017-10-30 22:31 ` Kees Cook
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Tobin C. Harding @ 2017-10-29 22:59 UTC (permalink / raw)
  To: kernel-hardening
  Cc: Tobin C. Harding, Jason A. Donenfeld, Theodore Ts'o,
	Linus Torvalds, Kees Cook, Paolo Bonzini, Tycho Andersen,
	Roberts, William C, Tejun Heo, Jordan Glover, Greg KH,
	Petr Mladek, Joe Perches, Ian Campbell, Sergey Senozhatsky,
	Catalin Marinas, Will Deacon, Steven Rostedt, Chris Fries,
	Dave Weinstein, Daniel Micay, Djalal Harouni, linux-kernel

Currently there are many places in the kernel where addresses are being
printed using an unadorned %p. Kernel pointers should be printed using
%pK allowing some control via the kptr_restrict sysctl. Exposing addresses
gives attackers sensitive information about the kernel layout in memory.

We can reduce the attack surface by hashing all addresses printed with
%p. This will of course break some users, forcing code printing needed
addresses to be updated.

For what it's worth, usage of unadorned %p can be broken down as
follows (thanks to Joe Perches).

$ git grep -E '%p[^A-Za-z0-9]' | cut -f1 -d"/" | sort | uniq -c
   1084 arch
     20 block
     10 crypto
     32 Documentation
   8121 drivers
   1221 fs
    143 include
    101 kernel
     69 lib
    100 mm
   1510 net
     40 samples
      7 scripts
     11 security
    166 sound
    152 tools
      2 virt

Add function ptr_to_id() to map an address to a 32 bit unique
identifier. Hash any unadorned usage of specifier %p and any malformed
specifiers. 

Signed-off-by: Tobin C. Harding <me@tobin.cc>

---

It seems we don't have consensus on a couple of things

1. The size of the hashed address on 64 bit architectures.
2. The use of '0x' pre-fix for hashed addresses.

In regards to (1), we are agreed that we only need 32 bits of
information. There is some questions however that outputting _only_ 32
bits may break userland.

In regards to (2), irrespective of the arguments for and against, if
point 1 is correct and changing the format will break userland then we
can't add the '0x' suffix for the same reason.

Therefore this patch masks off the first 32 bits, retaining
only 32 bits of information. We do not add a '0x' suffix. All in all,
that results in _no_ change to the format of output only the content of
the output.

The leading 0's also make explicit that we have messed with the address,
maybe this will save some debugging time by doing so. Although this
would probably already be obvious since there is no leading 'ffff'.

We hash malformed specifiers also. Malformed specifiers include
incomplete (e.g %pi) and also non-existent specifiers. checkpatch should
warn for non-existent specifiers but AFAICT won't warn for incomplete
specifiers.

Here is the behaviour that this patch implements.

For kpt_restrict==0

Randomness not ready:
  printed with %p: 		(pointer value)     # NOTE: with padding
Valid pointer:
  printed with %pK: 		deadbeefdeadbeef
  printed with %p: 		00000000deadbeef
  malformed specifier (eg %i):  00000000deadbeef
NULL pointer:
  printed with %pK: 		0000000000000000
  printed with %p: 		         (null)     # NOTE: with padding
  malformed specifier (eg %i):           (null)

For kpt_restrict==2

Valid pointer:
  printed with %pK: 		0000000000000000

All other output as for kptr_restrict==0

V9:
 - Drop the initial patch from V8, leaving null pointer handling as is.
 - Print the hashed ID _without_ a '0x' suffix.
 - Mask the first 32 bits of the hashed ID to all zeros on 64 bit
   architectures.

V8:
 - Add second patch cleaning up null pointer printing in pointer()
 - Move %pK handling to separate function, further cleaning up pointer()
 - Move ptr_to_id() call outside of switch statement making hashing
   the default behaviour (including malformed specifiers).
 - Remove use of static_key, replace with simple boolean.

V7:
 - Use tabs instead of spaces (ouch!).

V6:
 - Use __early_initcall() to fill the SipHash key.
 - Use static keys to guard hashing before the key is available.

V5:
 - Remove spin lock.
 - Add Jason A. Donenfeld to CC list by request.
 - Add Theodore Ts'o to CC list due to comment on previous version.

V4:
 - Remove changes to siphash.{ch}
 - Do word size check, and return value cast, directly in ptr_to_id().
 - Use add_ready_random_callback() to guard call to get_random_bytes()

V3:
 - Use atomic_xchg() to guard setting [random] key.
 - Remove erroneous white space change.

V2:
 - Use SipHash to do the hashing.

The discussion related to this patch has been fragmented. There are
three threads associated with this patch. Email threads by subject:

[PATCH] printk: hash addresses printed with %p
[PATCH 0/3] add %pX specifier
[kernel-hardening] [RFC V2 0/6] add more kernel pointer filter options

 lib/vsprintf.c | 167 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 119 insertions(+), 48 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 86c3385b9eb3..0c9a008fc256 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -33,6 +33,8 @@
 #include <linux/uuid.h>
 #include <linux/of.h>
 #include <net/addrconf.h>
+#include <linux/siphash.h>
+#include <linux/compiler.h>
 #ifdef CONFIG_BLOCK
 #include <linux/blkdev.h>
 #endif
@@ -1344,6 +1346,57 @@ char *uuid_string(char *buf, char *end, const u8 *addr,
 }
 
 static noinline_for_stack
+char *kernel_pointer(char *buf, char *end, const void *ptr,
+		     struct printf_spec spec)
+{
+	spec.base = 16;
+	spec.flags |= SMALL;
+	if (spec.field_width == -1) {
+		spec.field_width = 2 * sizeof(void *);
+		spec.flags |= ZEROPAD;
+	}
+
+	switch (kptr_restrict) {
+	case 0:
+		/* Always print %pK values */
+		break;
+	case 1: {
+		const struct cred *cred;
+
+		/*
+		 * kptr_restrict==1 cannot be used in IRQ context
+		 * because its test for CAP_SYSLOG would be meaningless.
+		 */
+		if (in_irq() || in_serving_softirq() || in_nmi())
+			return string(buf, end, "pK-error", spec);
+
+		/*
+		 * Only print the real pointer value if the current
+		 * process has CAP_SYSLOG and is running with the
+		 * same credentials it started with. This is because
+		 * access to files is checked at open() time, but %pK
+		 * checks permission at read() time. We don't want to
+		 * leak pointer values if a binary opens a file using
+		 * %pK and then elevates privileges before reading it.
+		 */
+		cred = current_cred();
+		if (!has_capability_noaudit(current, CAP_SYSLOG) ||
+		    !uid_eq(cred->euid, cred->uid) ||
+		    !gid_eq(cred->egid, cred->gid))
+			ptr = NULL;
+		break;
+	}
+	case 2:
+	default:
+		/* Always print 0's for %pK */
+		ptr = NULL;
+		break;
+	}
+
+	return number(buf, end, (unsigned long)ptr, spec);
+}
+
+static noinline_for_stack
 char *netdev_bits(char *buf, char *end, const void *addr, const char *fmt)
 {
 	unsigned long long num;
@@ -1591,6 +1644,66 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
 	return widen_string(buf, buf - buf_start, end, spec);
 }
 
+static bool have_filled_random_ptr_key __read_mostly;
+static siphash_key_t ptr_key __read_mostly;
+
+static void fill_random_ptr_key(struct random_ready_callback *unused)
+{
+	get_random_bytes(&ptr_key, sizeof(ptr_key));
+	WRITE_ONCE(have_filled_random_ptr_key, true);
+}
+
+static struct random_ready_callback random_ready = {
+	.func = fill_random_ptr_key
+};
+
+static int __init initialize_ptr_random(void)
+{
+	int ret = add_random_ready_callback(&random_ready);
+
+	if (!ret)
+		return 0;
+	else if (ret == -EALREADY) {
+		fill_random_ptr_key(&random_ready);
+		return 0;
+	}
+
+	return ret;
+}
+early_initcall(initialize_ptr_random);
+
+/* Maps a pointer to a 32 bit unique identifier. */
+static char *ptr_to_id(char *buf, char *end, void *ptr, struct printf_spec spec)
+{
+	unsigned long hashval;
+	const int default_width = 2 * sizeof(void *);
+
+	if (unlikely(!have_filled_random_ptr_key)) {
+		spec.field_width = default_width;
+		return string(buf, end, "(pointer value)", spec);
+	}
+
+#ifdef CONFIG_64BIT
+	hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key);
+	/*
+	 * Mask off the first 32 bits, this makes explicit that we have
+	 * modified the address (and 32 bits is plenty for a unique ID).
+	 */
+	hashval = hashval & 0xffffffff;
+#else
+	hashval = (unsigned long)siphash_1u32((u32)ptr, &ptr_key);
+#endif
+
+	spec.flags |= SMALL;
+	if (spec.field_width == -1) {
+		spec.field_width = default_width;
+		spec.flags |= ZEROPAD;
+	}
+	spec.base = 16;
+
+	return number(buf, end, hashval, spec);
+}
+
 int kptr_restrict __read_mostly;
 
 /*
@@ -1703,6 +1816,9 @@ int kptr_restrict __read_mostly;
  * Note: The difference between 'S' and 'F' is that on ia64 and ppc64
  * function pointers are really function descriptors, which contain a
  * pointer to the real address.
+ *
+ * Note: The default behaviour (unadorned %p) is to hash the address,
+ * rendering it useful as a unique identifier.
  */
 static noinline_for_stack
 char *pointer(const char *fmt, char *buf, char *end, void *ptr,
@@ -1792,47 +1908,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 			return buf;
 		}
 	case 'K':
-		switch (kptr_restrict) {
-		case 0:
-			/* Always print %pK values */
-			break;
-		case 1: {
-			const struct cred *cred;
-
-			/*
-			 * kptr_restrict==1 cannot be used in IRQ context
-			 * because its test for CAP_SYSLOG would be meaningless.
-			 */
-			if (in_irq() || in_serving_softirq() || in_nmi()) {
-				if (spec.field_width == -1)
-					spec.field_width = default_width;
-				return string(buf, end, "pK-error", spec);
-			}
-
-			/*
-			 * Only print the real pointer value if the current
-			 * process has CAP_SYSLOG and is running with the
-			 * same credentials it started with. This is because
-			 * access to files is checked at open() time, but %pK
-			 * checks permission at read() time. We don't want to
-			 * leak pointer values if a binary opens a file using
-			 * %pK and then elevates privileges before reading it.
-			 */
-			cred = current_cred();
-			if (!has_capability_noaudit(current, CAP_SYSLOG) ||
-			    !uid_eq(cred->euid, cred->uid) ||
-			    !gid_eq(cred->egid, cred->gid))
-				ptr = NULL;
-			break;
-		}
-		case 2:
-		default:
-			/* Always print 0's for %pK */
-			ptr = NULL;
-			break;
-		}
-		break;
-
+		return kernel_pointer(buf, end, ptr, spec);
 	case 'N':
 		return netdev_bits(buf, end, ptr, fmt);
 	case 'a':
@@ -1858,14 +1934,9 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 			return device_node_string(buf, end, ptr, spec, fmt + 1);
 		}
 	}
-	spec.flags |= SMALL;
-	if (spec.field_width == -1) {
-		spec.field_width = default_width;
-		spec.flags |= ZEROPAD;
-	}
-	spec.base = 16;
 
-	return number(buf, end, (unsigned long) ptr, spec);
+	/* default is to _not_ leak addresses, hash before printing */
+	return ptr_to_id(buf, end, ptr, spec);
 }
 
 /*
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH V9] printk: hash addresses printed with %p
  2017-10-29 22:59 [PATCH V9] printk: hash addresses printed with %p Tobin C. Harding
@ 2017-10-30 22:31 ` Kees Cook
  2017-10-30 22:45   ` Tobin C. Harding
  2017-10-31 15:39 ` Petr Mladek
  2017-11-01  0:50 ` kbuild test robot
  2 siblings, 1 reply; 7+ messages in thread
From: Kees Cook @ 2017-10-30 22:31 UTC (permalink / raw)
  To: Tobin C. Harding
  Cc: kernel-hardening, Jason A. Donenfeld, Theodore Ts'o,
	Linus Torvalds, Paolo Bonzini, Tycho Andersen, Roberts,
	William C, Tejun Heo, Jordan Glover, Greg KH, Petr Mladek,
	Joe Perches, Ian Campbell, Sergey Senozhatsky, Catalin Marinas,
	Will Deacon, Steven Rostedt, Chris Fries, Dave Weinstein,
	Daniel Micay, Djalal Harouni, LKML

On Sun, Oct 29, 2017 at 3:59 PM, Tobin C. Harding <me@tobin.cc> wrote:
> Currently there are many places in the kernel where addresses are being
> printed using an unadorned %p. Kernel pointers should be printed using
> %pK allowing some control via the kptr_restrict sysctl. Exposing addresses
> gives attackers sensitive information about the kernel layout in memory.
>
> We can reduce the attack surface by hashing all addresses printed with
> %p. This will of course break some users, forcing code printing needed
> addresses to be updated.
>
> For what it's worth, usage of unadorned %p can be broken down as
> follows (thanks to Joe Perches).
>
> $ git grep -E '%p[^A-Za-z0-9]' | cut -f1 -d"/" | sort | uniq -c
>    1084 arch
>      20 block
>      10 crypto
>      32 Documentation
>    8121 drivers
>    1221 fs
>     143 include
>     101 kernel
>      69 lib
>     100 mm
>    1510 net
>      40 samples
>       7 scripts
>      11 security
>     166 sound
>     152 tools
>       2 virt
>
> Add function ptr_to_id() to map an address to a 32 bit unique
> identifier. Hash any unadorned usage of specifier %p and any malformed
> specifiers.
>
> Signed-off-by: Tobin C. Harding <me@tobin.cc>
>
> ---
>
> It seems we don't have consensus on a couple of things
>
> 1. The size of the hashed address on 64 bit architectures.
> 2. The use of '0x' pre-fix for hashed addresses.
>
> In regards to (1), we are agreed that we only need 32 bits of
> information. There is some questions however that outputting _only_ 32
> bits may break userland.
>
> In regards to (2), irrespective of the arguments for and against, if
> point 1 is correct and changing the format will break userland then we
> can't add the '0x' suffix for the same reason.
>
> Therefore this patch masks off the first 32 bits, retaining
> only 32 bits of information. We do not add a '0x' suffix. All in all,
> that results in _no_ change to the format of output only the content of
> the output.
>
> The leading 0's also make explicit that we have messed with the address,
> maybe this will save some debugging time by doing so. Although this
> would probably already be obvious since there is no leading 'ffff'.
>
> We hash malformed specifiers also. Malformed specifiers include
> incomplete (e.g %pi) and also non-existent specifiers. checkpatch should
> warn for non-existent specifiers but AFAICT won't warn for incomplete
> specifiers.
>
> Here is the behaviour that this patch implements.
>
> For kpt_restrict==0
>
> Randomness not ready:
>   printed with %p:              (pointer value)     # NOTE: with padding
> Valid pointer:
>   printed with %pK:             deadbeefdeadbeef
>   printed with %p:              00000000deadbeef
>   malformed specifier (eg %i):  00000000deadbeef
> NULL pointer:
>   printed with %pK:             0000000000000000
>   printed with %p:                       (null)     # NOTE: with padding
>   malformed specifier (eg %i):           (null)
>
> For kpt_restrict==2
>
> Valid pointer:
>   printed with %pK:             0000000000000000
>
> All other output as for kptr_restrict==0
>
> V9:
>  - Drop the initial patch from V8, leaving null pointer handling as is.
>  - Print the hashed ID _without_ a '0x' suffix.
>  - Mask the first 32 bits of the hashed ID to all zeros on 64 bit
>    architectures.

Oops, I had missed v9. This addresses my concerns. I think the leading
zeros are a good way to identify the "this is clearly not a kernel
address" issue (though the 32-bit folks may remain confused, but we
can fix that later, IMO).

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V9] printk: hash addresses printed with %p
  2017-10-30 22:31 ` Kees Cook
@ 2017-10-30 22:45   ` Tobin C. Harding
  0 siblings, 0 replies; 7+ messages in thread
From: Tobin C. Harding @ 2017-10-30 22:45 UTC (permalink / raw)
  To: Kees Cook
  Cc: kernel-hardening, Jason A. Donenfeld, Theodore Ts'o,
	Linus Torvalds, Paolo Bonzini, Tycho Andersen, Roberts,
	William C, Tejun Heo, Jordan Glover, Greg KH, Petr Mladek,
	Joe Perches, Ian Campbell, Sergey Senozhatsky, Catalin Marinas,
	Will Deacon, Steven Rostedt, Chris Fries, Dave Weinstein,
	Daniel Micay, Djalal Harouni, LKML

On Mon, Oct 30, 2017 at 03:31:41PM -0700, Kees Cook wrote:
> On Sun, Oct 29, 2017 at 3:59 PM, Tobin C. Harding <me@tobin.cc> wrote:
> > Currently there are many places in the kernel where addresses are being
> > printed using an unadorned %p. Kernel pointers should be printed using
> > %pK allowing some control via the kptr_restrict sysctl. Exposing addresses
> > gives attackers sensitive information about the kernel layout in memory.
> >
> > We can reduce the attack surface by hashing all addresses printed with
> > %p. This will of course break some users, forcing code printing needed
> > addresses to be updated.
> >
> > For what it's worth, usage of unadorned %p can be broken down as
> > follows (thanks to Joe Perches).
> >
> > $ git grep -E '%p[^A-Za-z0-9]' | cut -f1 -d"/" | sort | uniq -c
> >    1084 arch
> >      20 block
> >      10 crypto
> >      32 Documentation
> >    8121 drivers
> >    1221 fs
> >     143 include
> >     101 kernel
> >      69 lib
> >     100 mm
> >    1510 net
> >      40 samples
> >       7 scripts
> >      11 security
> >     166 sound
> >     152 tools
> >       2 virt
> >
> > Add function ptr_to_id() to map an address to a 32 bit unique
> > identifier. Hash any unadorned usage of specifier %p and any malformed
> > specifiers.
> >
> > Signed-off-by: Tobin C. Harding <me@tobin.cc>
> >
> > ---
> >
> > It seems we don't have consensus on a couple of things
> >
> > 1. The size of the hashed address on 64 bit architectures.
> > 2. The use of '0x' pre-fix for hashed addresses.
> >
> > In regards to (1), we are agreed that we only need 32 bits of
> > information. There is some questions however that outputting _only_ 32
> > bits may break userland.
> >
> > In regards to (2), irrespective of the arguments for and against, if
> > point 1 is correct and changing the format will break userland then we
> > can't add the '0x' suffix for the same reason.
> >
> > Therefore this patch masks off the first 32 bits, retaining
> > only 32 bits of information. We do not add a '0x' suffix. All in all,
> > that results in _no_ change to the format of output only the content of
> > the output.
> >
> > The leading 0's also make explicit that we have messed with the address,
> > maybe this will save some debugging time by doing so. Although this
> > would probably already be obvious since there is no leading 'ffff'.
> >
> > We hash malformed specifiers also. Malformed specifiers include
> > incomplete (e.g %pi) and also non-existent specifiers. checkpatch should
> > warn for non-existent specifiers but AFAICT won't warn for incomplete
> > specifiers.
> >
> > Here is the behaviour that this patch implements.
> >
> > For kpt_restrict==0
> >
> > Randomness not ready:
> >   printed with %p:              (pointer value)     # NOTE: with padding
> > Valid pointer:
> >   printed with %pK:             deadbeefdeadbeef
> >   printed with %p:              00000000deadbeef
> >   malformed specifier (eg %i):  00000000deadbeef
> > NULL pointer:
> >   printed with %pK:             0000000000000000
> >   printed with %p:                       (null)     # NOTE: with padding
> >   malformed specifier (eg %i):           (null)
> >
> > For kpt_restrict==2
> >
> > Valid pointer:
> >   printed with %pK:             0000000000000000
> >
> > All other output as for kptr_restrict==0
> >
> > V9:
> >  - Drop the initial patch from V8, leaving null pointer handling as is.
> >  - Print the hashed ID _without_ a '0x' suffix.
> >  - Mask the first 32 bits of the hashed ID to all zeros on 64 bit
> >    architectures.
> 
> Oops, I had missed v9. This addresses my concerns. I think the leading
> zeros are a good way to identify the "this is clearly not a kernel
> address" issue (though the 32-bit folks may remain confused, but we
> can fix that later, IMO).

Awesome. Yeah this patch (coupled with the leaking_addresses.pl script)
is turning out to be a bit 64-bit centric. However, as we plug more
leaks in 64-bit kernels hopefully they will be plugged in 32-bit ones
too.

I can't think of any way to have leaking_addresses.pl grep for 32-bit
addresses, especially once/if this patch gets merged. We will not be
able to differentiate between hashed addresses and real addresses on
32-bit machines.

thanks,
Tobin.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V9] printk: hash addresses printed with %p
  2017-10-29 22:59 [PATCH V9] printk: hash addresses printed with %p Tobin C. Harding
  2017-10-30 22:31 ` Kees Cook
@ 2017-10-31 15:39 ` Petr Mladek
  2017-10-31 23:53   ` Tobin C. Harding
  2017-11-01  0:50 ` kbuild test robot
  2 siblings, 1 reply; 7+ messages in thread
From: Petr Mladek @ 2017-10-31 15:39 UTC (permalink / raw)
  To: Tobin C. Harding
  Cc: kernel-hardening, Jason A. Donenfeld, Theodore Ts'o,
	Linus Torvalds, Kees Cook, Paolo Bonzini, Tycho Andersen,
	Roberts, William C, Tejun Heo, Jordan Glover, Greg KH,
	Joe Perches, Ian Campbell, Sergey Senozhatsky, Catalin Marinas,
	Will Deacon, Steven Rostedt, Chris Fries, Dave Weinstein,
	Daniel Micay, Djalal Harouni, linux-kernel

On Mon 2017-10-30 09:59:16, Tobin C. Harding wrote:
> Currently there are many places in the kernel where addresses are being
> printed using an unadorned %p. Kernel pointers should be printed using
> %pK allowing some control via the kptr_restrict sysctl. Exposing addresses
> gives attackers sensitive information about the kernel layout in memory.
> 
> We can reduce the attack surface by hashing all addresses printed with
> %p. This will of course break some users, forcing code printing needed
> addresses to be updated.

I am sorry for my ignorance but what is the right update, please?
I expect that there are several possibilities:

  + remove the pointer at all

  + replace it with %pK so that it honors kptr_restrict setting

  + any other option?

Is kptr_restrict considered a safe mechanism?

Also kptr_restrict seems to be primary for the messages that are available
via /proc and /sys. Is it good enough for the messages logged by
printk()?

Will there be a debug option that would allow to see the original
pointers? Or what is the preferred way for debug messages?


> For what it's worth, usage of unadorned %p can be broken down as
> follows (thanks to Joe Perches).
> 
> $ git grep -E '%p[^A-Za-z0-9]' | cut -f1 -d"/" | sort | uniq -c
>    1084 arch
>      20 block
>      10 crypto
>      32 Documentation
>    8121 drivers
>    1221 fs
>     143 include
>     101 kernel
>      69 lib
>     100 mm
>    1510 net
>      40 samples
>       7 scripts
>      11 security
>     166 sound
>     152 tools
>       2 virt

It is evident that it will hit many people. I guess that they will
be suprised and might have similar questions. It might make sense
to decribe this in Documentation/printk-formats.txt.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V9] printk: hash addresses printed with %p
  2017-10-31 15:39 ` Petr Mladek
@ 2017-10-31 23:53   ` Tobin C. Harding
  2017-11-01 12:43     ` Petr Mladek
  0 siblings, 1 reply; 7+ messages in thread
From: Tobin C. Harding @ 2017-10-31 23:53 UTC (permalink / raw)
  To: Petr Mladek
  Cc: kernel-hardening, Jason A. Donenfeld, Theodore Ts'o,
	Linus Torvalds, Kees Cook, Paolo Bonzini, Tycho Andersen,
	Roberts, William C, Tejun Heo, Jordan Glover, Greg KH,
	Joe Perches, Ian Campbell, Sergey Senozhatsky, Catalin Marinas,
	Will Deacon, Steven Rostedt, Chris Fries, Dave Weinstein,
	Daniel Micay, Djalal Harouni, linux-kernel

On Tue, Oct 31, 2017 at 04:39:44PM +0100, Petr Mladek wrote:
> On Mon 2017-10-30 09:59:16, Tobin C. Harding wrote:
> > Currently there are many places in the kernel where addresses are being
> > printed using an unadorned %p. Kernel pointers should be printed using
> > %pK allowing some control via the kptr_restrict sysctl. Exposing addresses
> > gives attackers sensitive information about the kernel layout in memory.
> > 
> > We can reduce the attack surface by hashing all addresses printed with
> > %p. This will of course break some users, forcing code printing needed
> > addresses to be updated.
> 
> I am sorry for my ignorance but what is the right update, please?

Can I say first that I am in no way an expert, I am new to both this
problem and kernel dev in general.

> I expect that there are several possibilities:
> 
>   + remove the pointer at all

This definitely stops the leak!

>   + replace it with %pK so that it honors kptr_restrict setting

I think this is the option of choice, see concerns below however. I get
the feeling that the hope with this patch is that a vast majority of
users of %p won't care so stopping all those addresses is the real win
for this patch. 

The next hoped benefit is that the hashing will shed light on this topic
and get developers to think about the issue before _wildly_ printing
addresses. Having to work harder to print the address in future will aid
this (assuming everyone doesn't just start using %x).

>   + any other option?

Use %x or %X - really bad, this will create more pain in the future.

> Is kptr_restrict considered a safe mechanism?
> 
> Also kptr_restrict seems to be primary for the messages that are available
> via /proc and /sys. Is it good enough for the messages logged by
> printk()?

There is some concern that kptr_restrict is not overly great. Linus is
the main purveyor of this argument. I won't paraphrase here because I
will not do the argument justice.

See this thread for the whole discussion

[kernel-hardening] [RFC V2 0/6] add more kernel pointer filter options

Side note: If this (mentioning a previous thread) is bad form, or there
is a better way to do it, could you please tell me.

> Will there be a debug option that would allow to see the original
> pointers? Or what is the preferred way for debug messages?

It seems to me that there is a fundamental conflict between security and
debugging here. To debug we need the addresses, for a secure kernel we
need there to be _no_ addresses shown to user space.

I don't think we have the final solution to this at the moment.

> > For what it's worth, usage of unadorned %p can be broken down as
> > follows (thanks to Joe Perches).
> > 
> > $ git grep -E '%p[^A-Za-z0-9]' | cut -f1 -d"/" | sort | uniq -c
> >    1084 arch
> >      20 block
> >      10 crypto
> >      32 Documentation
> >    8121 drivers
> >    1221 fs
> >     143 include
> >     101 kernel
> >      69 lib
> >     100 mm
> >    1510 net
> >      40 samples
> >       7 scripts
> >      11 security
> >     166 sound
> >     152 tools
> >       2 virt
> 
> It is evident that it will hit many people. I guess that they will
> be suprised and might have similar questions. It might make sense
> to decribe this in Documentation/printk-formats.txt.

Very good idea, V10 to include.

thanks,
Tobin.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V9] printk: hash addresses printed with %p
  2017-10-29 22:59 [PATCH V9] printk: hash addresses printed with %p Tobin C. Harding
  2017-10-30 22:31 ` Kees Cook
  2017-10-31 15:39 ` Petr Mladek
@ 2017-11-01  0:50 ` kbuild test robot
  2 siblings, 0 replies; 7+ messages in thread
From: kbuild test robot @ 2017-11-01  0:50 UTC (permalink / raw)
  To: Tobin C. Harding
  Cc: kbuild-all, kernel-hardening, Tobin C. Harding,
	Jason A. Donenfeld, Theodore Ts'o, Linus Torvalds, Kees Cook,
	Paolo Bonzini, Tycho Andersen, Roberts, William C, Tejun Heo,
	Jordan Glover, Greg KH, Petr Mladek, Joe Perches, Ian Campbell,
	Sergey Senozhatsky, Catalin Marinas, Will Deacon, Steven Rostedt,
	Chris Fries, Dave Weinstein, Daniel Micay, Djalal Harouni,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2787 bytes --]

Hi Tobin,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.14-rc7 next-20171018]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Tobin-C-Harding/printk-hash-addresses-printed-with-p/20171101-080718
config: i386-tinyconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   lib/vsprintf.c: In function 'kernel_pointer':
>> lib/vsprintf.c:1359:10: error: 'kptr_restrict' undeclared (first use in this function)
     switch (kptr_restrict) {
             ^~~~~~~~~~~~~
   lib/vsprintf.c:1359:10: note: each undeclared identifier is reported only once for each function it appears in

vim +/kptr_restrict +1359 lib/vsprintf.c

  1347	
  1348	static noinline_for_stack
  1349	char *kernel_pointer(char *buf, char *end, const void *ptr,
  1350			     struct printf_spec spec)
  1351	{
  1352		spec.base = 16;
  1353		spec.flags |= SMALL;
  1354		if (spec.field_width == -1) {
  1355			spec.field_width = 2 * sizeof(void *);
  1356			spec.flags |= ZEROPAD;
  1357		}
  1358	
> 1359		switch (kptr_restrict) {
  1360		case 0:
  1361			/* Always print %pK values */
  1362			break;
  1363		case 1: {
  1364			const struct cred *cred;
  1365	
  1366			/*
  1367			 * kptr_restrict==1 cannot be used in IRQ context
  1368			 * because its test for CAP_SYSLOG would be meaningless.
  1369			 */
  1370			if (in_irq() || in_serving_softirq() || in_nmi())
  1371				return string(buf, end, "pK-error", spec);
  1372	
  1373			/*
  1374			 * Only print the real pointer value if the current
  1375			 * process has CAP_SYSLOG and is running with the
  1376			 * same credentials it started with. This is because
  1377			 * access to files is checked at open() time, but %pK
  1378			 * checks permission at read() time. We don't want to
  1379			 * leak pointer values if a binary opens a file using
  1380			 * %pK and then elevates privileges before reading it.
  1381			 */
  1382			cred = current_cred();
  1383			if (!has_capability_noaudit(current, CAP_SYSLOG) ||
  1384			    !uid_eq(cred->euid, cred->uid) ||
  1385			    !gid_eq(cred->egid, cred->gid))
  1386				ptr = NULL;
  1387			break;
  1388		}
  1389		case 2:
  1390		default:
  1391			/* Always print 0's for %pK */
  1392			ptr = NULL;
  1393			break;
  1394		}
  1395	
  1396		return number(buf, end, (unsigned long)ptr, spec);
  1397	}
  1398	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6685 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V9] printk: hash addresses printed with %p
  2017-10-31 23:53   ` Tobin C. Harding
@ 2017-11-01 12:43     ` Petr Mladek
  0 siblings, 0 replies; 7+ messages in thread
From: Petr Mladek @ 2017-11-01 12:43 UTC (permalink / raw)
  To: Tobin C. Harding
  Cc: kernel-hardening, Jason A. Donenfeld, Theodore Ts'o,
	Linus Torvalds, Kees Cook, Paolo Bonzini, Tycho Andersen,
	Roberts, William C, Tejun Heo, Jordan Glover, Greg KH,
	Joe Perches, Ian Campbell, Sergey Senozhatsky, Catalin Marinas,
	Will Deacon, Steven Rostedt, Chris Fries, Dave Weinstein,
	Daniel Micay, Djalal Harouni, linux-kernel

On Wed 2017-11-01 10:53:45, Tobin C. Harding wrote:
> On Tue, Oct 31, 2017 at 04:39:44PM +0100, Petr Mladek wrote:
> > On Mon 2017-10-30 09:59:16, Tobin C. Harding wrote:
> > > Currently there are many places in the kernel where addresses are being
> > > printed using an unadorned %p. Kernel pointers should be printed using
> > > %pK allowing some control via the kptr_restrict sysctl. Exposing addresses
> > > gives attackers sensitive information about the kernel layout in memory.
> > > 
> > > We can reduce the attack surface by hashing all addresses printed with
> > > %p. This will of course break some users, forcing code printing needed
> > > addresses to be updated.
> > 
> > I am sorry for my ignorance but what is the right update, please?
> 
> Can I say first that I am in no way an expert, I am new to both this
> problem and kernel dev in general.

Sure. There are many experienced people in CC. I hope that they might
have some opinion on this.

My concern is that this patch breaks some functionality because it
is dangerous. This makes perfect sense. But people will want to fix
it a safe way. And the way is not clear to me.


> > I expect that there are several possibilities:
> > 
> >   + remove the pointer at all
> 
> This definitely stops the leak!

Sure. But this is not a solution for debugging tools, e.g. lockdep.
sysrq-related dumps. Of course, the pointers must not be visible
on production system to normal users but there should be a way
to see them for debugging purposes.

> >   + replace it with %pK so that it honors kptr_restrict setting
> 
> I think this is the option of choice, see concerns below however. I get
> the feeling that the hope with this patch is that a vast majority of
> users of %p won't care so stopping all those addresses is the real win
> for this patch.
> 
> The next hoped benefit is that the hashing will shed light on this topic
> and get developers to think about the issue before _wildly_ printing
> addresses. Having to work harder to print the address in future will aid
> this (assuming everyone doesn't just start using %x).



> >   + any other option?
> 
> Use %x or %X - really bad, this will create more pain in the future.

Yes, using %x or %X is dangerous. It would be used by users
that do not mind about security. Or is there any situation when
always using the original pointer as a string is needed
for a real functionality?

IMHO, string is needed only for human readable usage. Then it always
smells with information leak.


> > Is kptr_restrict considered a safe mechanism?
> > 
> > Also kptr_restrict seems to be primary for the messages that are available
> > via /proc and /sys. Is it good enough for the messages logged by
> > printk()?
> 
> There is some concern that kptr_restrict is not overly great. Linus is
> the main purveyor of this argument. I won't paraphrase here because I
> will not do the argument justice.
> 
> See this thread for the whole discussion
> 
> [kernel-hardening] [RFC V2 0/6] add more kernel pointer filter options

I saw the discussion but it was a bit hard to follow. I would
highlight the following three concerns related to kptr_restrict:

First, it did not help to improve the situation. IMHO, this is mainly
because it was opt-in and did not force people to fix the code. This
is being addressed by this patch that actively breaks all users.

Second, the user credentials are checked when the string is formatted.
They do not reflect the path how the string is passed to the user.
Therefore it might be cheated. This opens a question if kptr_restrict
would stay in kernel and whether the conversion from %p to %pK
is one of the proposed solutions.

Third, kptr_restrict can be modified too late. It means that
the pointers printed during the early boot will never or always
be readable. It means that you would need two different kernel
builds for production and debugging. Well, this is probably
the smallest issue.


All in all. I wonder if it would make sense to introduce
some compiler or command line option that would allow
to disable the hashing of pointers for debugging
purposes.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-11-01 12:43 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-29 22:59 [PATCH V9] printk: hash addresses printed with %p Tobin C. Harding
2017-10-30 22:31 ` Kees Cook
2017-10-30 22:45   ` Tobin C. Harding
2017-10-31 15:39 ` Petr Mladek
2017-10-31 23:53   ` Tobin C. Harding
2017-11-01 12:43     ` Petr Mladek
2017-11-01  0:50 ` kbuild test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).