Skip to contentSkip to conversation

Checkpoint/Restore: Show in proc IDs of objects that can be shared between tasks

PATCH Superseded Pavel Emelyanov 6 participants 13 messages Nov 15, 2011 – Nov 18, 2011

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover Checkpoint/Restore: Show in proc IDs of objects that can be shared between tasks

While doing the checkpoint-restore in the userspace one need to determine whether various kernel objects (like mm_struct-s of file_struct-s) are shared between tasks and restore this state.

The 2nd step can for now be solved by using respective CLONE_XXX flags and the unshare syscall, while there's currently no ways for solving the 1st one.

One of the ways for checking whether two tasks share e.g. an mm_struct is to provide some mm_struct ID of a task to its proc file. The best from the performance point of view ID is the object address in the kernel, but showing them to the userspace is not good for performance reasons.

The previous attempt to solve this was to generate an ID for slab/slub and then mix it up with the object index on the slab page. This attempt wasn't met warmly by slab maintainers, so here's the 2nd approach.

The object address is XOR-ed with a "random" value of the same size and then shown in proc. Providing this poison is not leaked into the userspace then ID seem to be safe.

The other change from the previous set - this now includes patches from /proc to show the IDs generated. The objects for which the IDs are shown are:

* all namespaces living in /proc/pid/ns/
* open files (shown in /proc/pid/fdinfo/)
* objects, that can be shared with CLONE_XXX flags (except for namespaces)

#1 lore raw

PATCH 1/4 Routine for generating an safe ID for kernel pointer

The routine XORs the given pointer with a random value thus producing an ID (32 or 64 bit, depending on the arch) which can be shown even to unprivileged user space processes without risking of leaking kernel information.

It implies that it gets called when the random pool is ready for providing a random long value.

---
 include/linux/gen_object_ids.h |   12 ++++++++++++
 mm/Kconfig                     |    7 +++++++
 mm/Makefile                    |    1 +
 mm/gen_object_ids.c            |   21 +++++++++++++++++++++
 4 files changed, 41 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/gen_object_ids.h
 create mode 100644 mm/gen_object_ids.c
include/linux/gen_object_ids.h new file +12 -0
new file mode 100644
index 0000000..17981ae
@@ -0,0 +1,12 @@
+#ifndef __GEN_OBJECT_IDS_H__
+#define __GEN_OBJECT_IDS_H__
+
+#ifdef CONFIG_GENERIC_OBJECT_IDS
+unsigned long gen_object_id(void *ptr);
+#else
+static inline unsigned long gen_object_id(void *ptr)
+{
+	return 0;
+}
+#endif
+#endif
mm/Kconfig +7 -0
index f2f1ca1..1480cbf 100644
@@ -370,3 +370,10 @@ config CLEANCACHE
 	  in a negligible performance hit.
 
 	  If unsure, say Y to enable cleancache
+
+config GENERIC_OBJECT_IDS
+	bool "Enable generic object ids infrastructure"
+	default n
+	help
+	  Turn on the (quite simple) funtionality that can generate IDs for
+	  kernel objects which is safe to export to the userspace.
mm/Makefile +1 -0
index 836e416..155797a 100644
@@ -50,3 +50,4 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
 obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
 obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
+obj-$(CONFIG_GENERIC_OBJECT_IDS) += gen_object_ids.o
mm/gen_object_ids.c new file +21 -0
new file mode 100644
index 0000000..a75119b
@@ -0,0 +1,21 @@
+#include <linux/gen_object_ids.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+
+static unsigned long ptr_poison __read_mostly;
+static DEFINE_SPINLOCK(ptr_poison_lock);
+
+unsigned long gen_object_id(void *ptr)
+{
+	if (!ptr)
+		return 0;
+
+	if (unlikely(!ptr_poison)) {
+		spin_lock(&ptr_poison_lock);
+		if (!ptr_poison)
+			get_random_bytes(&ptr_poison, sizeof(ptr_poison));
+		spin_unlock(&ptr_poison_lock);
+	}
+
+	return ((unsigned long)ptr) ^ ptr_poison;
+}
Signature · 1 line
1.5.5.6
#2 lore raw

Re: [PATCH 1/4] Routine for generating an safe ID for kernel pointer

Quoted 78 lines · Pavel Emelyanov

The routine XORs the given pointer with a random value thus producing an ID (32 or 64 bit, depending on the arch) which can be shown even to unprivileged user space processes without risking of leaking kernel information.

It implies that it gets called when the random pool is ready for providing a random long value.

---
 include/linux/gen_object_ids.h |   12 ++++++++++++ mm/Kconfig                     |    7 +++++++ mm/Makefile                    |    1 + mm/gen_object_ids.c            |   21 +++++++++++++++++++++

 4 files changed, 41 insertions(+), 0 deletions(-)

 create mode 100644 include/linux/gen_object_ids.h create mode 100644 mm/gen_object_ids.c

include/linux/gen_object_ids.h new file +12 -0
new file mode 100644
index 0000000..17981ae
@@ -0,0 +1,12 @@
+#ifndef __GEN_OBJECT_IDS_H__
+#define __GEN_OBJECT_IDS_H__
+
+#ifdef CONFIG_GENERIC_OBJECT_IDS
+unsigned long gen_object_id(void *ptr);
+#else
+static inline unsigned long gen_object_id(void *ptr)
+{
+       return 0;
+}
+#endif
+#endif
mm/Kconfig +0 -0
index f2f1ca1..1480cbf 100644
@@ -370,3 +370,10 @@ config CLEANCACHE

         in a negligible performance hit.

         If unsure, say Y to enable cleancache

mm/Kconfig +7 -0
+
+config GENERIC_OBJECT_IDS
+       bool "Enable generic object ids infrastructure"
+       default n
+       help
+         Turn on the (quite simple) funtionality that can generate IDs for
+         kernel objects which is safe to export to the userspace.
mm/Makefile +0 -0
index 836e416..155797a 100644
@@ -50,3 +50,4 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o

 obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o obj-$(CONFIG_CLEANCACHE) += cleancache.o

mm/Makefile +1 -0
+obj-$(CONFIG_GENERIC_OBJECT_IDS) += gen_object_ids.o
mm/gen_object_ids.c new file +11 -0
new file mode 100644
index 0000000..a75119b
@@ -0,0 +1,21 @@
+#include <linux/gen_object_ids.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+
+static unsigned long ptr_poison __read_mostly;
+static DEFINE_SPINLOCK(ptr_poison_lock);
+
+unsigned long gen_object_id(void *ptr)
+{
+       if (!ptr)
+               return 0;

mm/gen_object_ids.c line 21

Quoted 10 lines
mm/gen_object_ids.c new file +10 -0
+
+       if (unlikely(!ptr_poison)) {
+               spin_lock(&ptr_poison_lock);
+               if (!ptr_poison)
+                       get_random_bytes(&ptr_poison, sizeof(ptr_poison));
+               spin_unlock(&ptr_poison_lock);
+       }
+
+       return ((unsigned long)ptr) ^ ptr_poison;
+}

You could put this in mm/util.c. Wouldn't it make sense to separate the initialization and use late_initcall() to call it?

Pekka

#3 lore raw

Re: [PATCH 1/4] Routine for generating an safe ID for kernel pointer

Quoted 9 lines
Quoted 9 lines
mm/gen_object_ids.c new file +9 -0
+unsigned long gen_object_id(void *ptr)
+{
+       if (!ptr)
+               return 0;
+
+       if (unlikely(!ptr_poison)) {
+               spin_lock(&ptr_poison_lock);
+               if (!ptr_poison)
+                       get_random_bytes(&ptr_poison, sizeof(ptr_poison));
Quoted 8 lines

mm/gen_object_ids.c line 21

Quoted 5 lines
mm/gen_object_ids.c new file +5 -0
+               spin_unlock(&ptr_poison_lock);
+       }
+
+       return ((unsigned long)ptr) ^ ptr_poison;
+}

You could put this in mm/util.c. Wouldn't it make sense to separate the initialization and use late_initcall() to call it?

OK, will put to util.c

About the initialization - I will put the sanity check about poison being not 0 on get_object_id() anyway, so what's the point in separate initialization?

Quoted 3 lines

Pekka
.

#4 lore raw

Re: [PATCH 1/4] Routine for generating an safe ID for kernel pointer

Quoted 18 lines · Pavel Emelyanov
Quoted 9 lines
Quoted 9 lines
mm/gen_object_ids.c new file +9 -0
+unsigned long gen_object_id(void *ptr)
+{
+       if (!ptr)
+               return 0;
+
+       if (unlikely(!ptr_poison)) {
+               spin_lock(&ptr_poison_lock);
+               if (!ptr_poison)
+                       get_random_bytes(&ptr_poison, sizeof(ptr_poison));
Quoted 8 lines

mm/gen_object_ids.c line 21

Quoted 5 lines

+               spin_unlock(&ptr_poison_lock);
+       }

mm/gen_object_ids.c new file +3 -0
+
+       return ((unsigned long)ptr) ^ ptr_poison;
+}

You could put this in mm/util.c. Wouldn't it make sense to separate the initialization and use late_initcall() to call it?

Quoted 4 lines

OK, will put to util.c

About the initialization - I will put the sanity check about poison being not 0 on get_object_id() anyway, so what's the point in separate initialization?

You no longer need the spinlock and you get rid of the potential double initialization problem because you're not holding the spinlock when you check for ptr_poisson being zero.

Pekka

#5 lore raw

Re: [PATCH 1/4] Routine for generating an safe ID for kernel pointer

In reply to Pavel Emelyanov
Quoted 78 lines · Pavel Emelyanov

The routine XORs the given pointer with a random value thus producing an ID (32 or 64 bit, depending on the arch) which can be shown even to unprivileged user space processes without risking of leaking kernel information.

It implies that it gets called when the random pool is ready for providing a random long value.

---
 include/linux/gen_object_ids.h |   12 ++++++++++++
 mm/Kconfig                     |    7 +++++++
 mm/Makefile                    |    1 +
 mm/gen_object_ids.c            |   21 +++++++++++++++++++++
 4 files changed, 41 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/gen_object_ids.h
 create mode 100644 mm/gen_object_ids.c
include/linux/gen_object_ids.h new file +12 -0
new file mode 100644
index 0000000..17981ae
@@ -0,0 +1,12 @@
+#ifndef __GEN_OBJECT_IDS_H__
+#define __GEN_OBJECT_IDS_H__
+
+#ifdef CONFIG_GENERIC_OBJECT_IDS
+unsigned long gen_object_id(void *ptr);
+#else
+static inline unsigned long gen_object_id(void *ptr)
+{
+	return 0;
+}
+#endif
+#endif
mm/Kconfig +7 -0
index f2f1ca1..1480cbf 100644
@@ -370,3 +370,10 @@ config CLEANCACHE
 	  in a negligible performance hit.
 
 	  If unsure, say Y to enable cleancache
+
+config GENERIC_OBJECT_IDS
+	bool "Enable generic object ids infrastructure"
+	default n
+	help
+	  Turn on the (quite simple) funtionality that can generate IDs for
+	  kernel objects which is safe to export to the userspace.
mm/Makefile +1 -0
index 836e416..155797a 100644
@@ -50,3 +50,4 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
 obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
 obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
+obj-$(CONFIG_GENERIC_OBJECT_IDS) += gen_object_ids.o
mm/gen_object_ids.c new file +11 -0
new file mode 100644
index 0000000..a75119b
@@ -0,0 +1,21 @@
+#include <linux/gen_object_ids.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+
+static unsigned long ptr_poison __read_mostly;
+static DEFINE_SPINLOCK(ptr_poison_lock);
+
+unsigned long gen_object_id(void *ptr)
+{
+	if (!ptr)
+		return 0;

mm/gen_object_ids.c line 21

Quoted 10 lines
mm/gen_object_ids.c new file +10 -0
+
+	if (unlikely(!ptr_poison)) {
+		spin_lock(&ptr_poison_lock);
+		if (!ptr_poison)
+			get_random_bytes(&ptr_poison, sizeof(ptr_poison));
+		spin_unlock(&ptr_poison_lock);
+	}
+
+	return ((unsigned long)ptr) ^ ptr_poison;
+}

It can not be called from irq context then...

I suggest using following code instead (no lock needed)

if (!ptr_poison) {
	unsigned long rnd;

	do {
		get_random_bytes(&rnd, sizeof(rnd));
	} while (rnd == 0);
	cmpxchg(&ptr_poison, 0, rnd);
}

return ((unsigned long)ptr) ^ ptr_poison;

#6 lore raw

Re: [PATCH 1/4] Routine for generating an safe ID for kernel pointer

In reply to Pavel Emelyanov

mm/gen_object_ids.c line 18

Quoted 11 lines · Pavel Emelyanov
mm/gen_object_ids.c new file +11 -0
+unsigned long gen_object_id(void *ptr)
+{
+	if (!ptr)
+		return 0;
+
+	if (unlikely(!ptr_poison)) {
+		spin_lock(&ptr_poison_lock);
+		if (!ptr_poison)
+			get_random_bytes(&ptr_poison, sizeof(ptr_poison));
+		spin_unlock(&ptr_poison_lock);
+	}

One thing that worries me about this is that there's one ptr_poison for all id's and any single leak of a pointer value will make all ids vulnerable. If we're going to do this, let's segregate different id spaces and use different poison values for each.

Thank you.

Signature · 1 line
tejun
#7 lore raw

PATCH 2/4 proc: Show namespaces IDs in /proc/pid/ns/* files

In reply to Pavel Emelyanov
---
 fs/proc/namespaces.c |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)
fs/proc/namespaces.c +12 -0
index be177f7..06baabb 100644
@@ -12,6 +12,7 @@
 #include <linux/mnt_namespace.h>
 #include <linux/ipc_namespace.h>
 #include <linux/pid_namespace.h>
+#include <linux/gen_object_ids.h>
 #include "internal.h"
 
 
@@ -27,8 +28,19 @@ static const struct proc_ns_operations *ns_entries[] = {
 #endif
 };
 
+static ssize_t proc_ns_read(struct file *file, char __user *buf,
+				      size_t len, loff_t *ppos)
+{
+	char tmp[32];
+	struct proc_inode *ei = PROC_I(file->f_dentry->d_inode);
+
+	snprintf(tmp, sizeof(tmp), "id:\t%lu\n", gen_object_id(ei->ns));
+	return simple_read_from_buffer(buf, len, ppos, tmp, strlen(tmp));
+}
+
 static const struct file_operations ns_file_operations = {
 	.llseek		= no_llseek,
+	.read		= proc_ns_read,
 };
 
 static struct dentry *proc_ns_instantiate(struct inode *dir,
Signature · 1 line
1.5.5.6
#8 lore raw

PATCH 3/4 proc: Show open file ID in /proc/pid/fdinfo/*

In reply to Pavel Emelyanov
---
 fs/proc/base.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)
fs/proc/base.c +4 -2
index 5eb0206..7a9e36b 100644
@@ -83,6 +83,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/fs_struct.h>
 #include <linux/slab.h>
+#include <linux/gen_object_ids.h>
 #ifdef CONFIG_HARDWALL
 #include <asm/hardwall.h>
 #endif
@@ -1934,9 +1935,10 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info)
 			if (info)
 				snprintf(info, PROC_FDINFO_MAX,
 					 "pos:\t%lli\n"
-					 "flags:\t0%o\n",
+					 "flags:\t0%o\n"
+					 "id:\t%lu\n",
 					 (long long) file->f_pos,
-					 f_flags);
+					 f_flags, gen_object_id(file));
 			spin_unlock(&files->file_lock);
 			put_files_struct(files);
 			return 0;
Signature · 1 line
1.5.5.6
#9 lore raw
In reply to Pavel Emelyanov
Quoted 11 lines · Pavel Emelyanov

While doing the checkpoint-restore in the userspace one need to determine whether various kernel objects (like mm_struct-s of file_struct-s) are shared between tasks and restore this state.

The 2nd step can for now be solved by using respective CLONE_XXX flags and the unshare syscall, while there's currently no ways for solving the 1st one.

One of the ways for checking whether two tasks share e.g. an mm_struct is to provide some mm_struct ID of a task to its proc file. The best from the performance point of view ID is the object address in the kernel, but showing them to the userspace is not good for performance reasons.

(I think you meant "not good for security reasons."...)

Quoted 7 lines

The previous attempt to solve this was to generate an ID for slab/slub and then mix it up with the object index on the slab page. This attempt wasn't met warmly by slab maintainers, so here's the 2nd approach.

The object address is XOR-ed with a "random" value of the same size and then shown in proc. Providing this poison is not leaked into the userspace then ID seem to be safe.

Really? There's no way to quickly derive the random number from known allocation patterns and thereby break the obfuscation scheme? To start we can note that the low N bits are directly exposed in the ID of anything that requires 2^N-byte alignment.

I think it's really a question of whether the high order bits can be derived.

And of course the random number only needs to be derived once per boot before it reveals the address of everything with an ID.

Some wild speculation:

I bet you could use some cpu affinity, mem policy, slab info, mmap tricks, etc. to derive more low bits of the random number. You can probably get even more when you consider objects that don't fit evenly in slabs. Speaking of slabs, is there some way to use the fact that nearby slab objects will share their high ID bits? If any of the ID-bearing objects allocated via kmalloc then inducing memory pressure and/or watching for buddy allocator merge/splits might reveal more low bits...

Cheers,
-Matt Helsley

#10 lore raw

On Tue, Nov 15, 2011 at 09:44:27PM -0800, Matt Helsley wrote:
...

Quoted 5 lines
Quoted 4 lines

The object address is XOR-ed with a "random" value of the same size and then shown in proc. Providing this poison is not leaked into the userspace then ID seem to be safe.

Quoted 7 lines

Really? There's no way to quickly derive the random number from known allocation patterns and thereby break the obfuscation scheme? To start we can note that the low N bits are directly exposed in the ID of anything that requires 2^N-byte alignment.

I think it's really a question of whether the high order bits can be derived.

Good point. I suppose we might use 2 random numbers here, one for xor and second to shuffle bits.

Quoted 2 lines

And of course the random number only needs to be derived once per boot before it reveals the address of everything with an ID.

Cyrill

#11 lore raw
In reply to Matt Helsley
Quoted 28 lines · Matt Helsley
Quoted 11 lines · Pavel Emelyanov

While doing the checkpoint-restore in the userspace one need to determine whether various kernel objects (like mm_struct-s of file_struct-s) are shared between tasks and restore this state.

The 2nd step can for now be solved by using respective CLONE_XXX flags and the unshare syscall, while there's currently no ways for solving the 1st one.

One of the ways for checking whether two tasks share e.g. an mm_struct is to provide some mm_struct ID of a task to its proc file. The best from the performance point of view ID is the object address in the kernel, but showing them to the userspace is not good for performance reasons.

(I think you meant "not good for security reasons."...)

Quoted 7 lines

The previous attempt to solve this was to generate an ID for slab/slub and then mix it up with the object index on the slab page. This attempt wasn't met warmly by slab maintainers, so here's the 2nd approach.

The object address is XOR-ed with a "random" value of the same size and then shown in proc. Providing this poison is not leaked into the userspace then ID seem to be safe.

Really? There's no way to quickly derive the random number from known allocation patterns and thereby break the obfuscation scheme? To start we can note that the low N bits are directly exposed in the ID of anything that requires 2^N-byte alignment.

Quoted 4 lines

I think it's really a question of whether the high order bits can be derived.

And of course the random number only needs to be derived once per boot before it reveals the address of everything with an ID.

Tejun already proposed to split ID space and use different poisons for them.

Quoted 7 lines

Some wild speculation:

I bet you could use some cpu affinity, mem policy, slab info, mmap tricks, etc. to derive more low bits of the random number. You can probably get even more when you consider objects that don't fit evenly in slabs. Speaking of slabs, is there some way to use the fact that nearby slab objects will share their high ID bits?

OK, let's assume we found out that two mm_struct IDs have higher bits equal, what can we do next to split address bits from the poison ones?

Quoted 9 lines

If any of the ID-bearing objects allocated via
kmalloc then inducing memory pressure and/or watching for buddy allocator merge/splits might reveal more low bits...

Cheers,
-Matt Helsley

.

#12 lore raw
Quoted 38 lines · Pavel Emelyanov
Quoted 28 lines · Matt Helsley
Quoted 11 lines · Pavel Emelyanov

While doing the checkpoint-restore in the userspace one need to determine whether various kernel objects (like mm_struct-s of file_struct-s) are shared between tasks and restore this state.

The 2nd step can for now be solved by using respective CLONE_XXX flags and the unshare syscall, while there's currently no ways for solving the 1st one.

One of the ways for checking whether two tasks share e.g. an mm_struct is to provide some mm_struct ID of a task to its proc file. The best from the performance point of view ID is the object address in the kernel, but showing them to the userspace is not good for performance reasons.

(I think you meant "not good for security reasons."...)

Quoted 7 lines

The previous attempt to solve this was to generate an ID for slab/slub and then mix it up with the object index on the slab page. This attempt wasn't met warmly by slab maintainers, so here's the 2nd approach.

The object address is XOR-ed with a "random" value of the same size and then shown in proc. Providing this poison is not leaked into the userspace then ID seem to be safe.

Really? There's no way to quickly derive the random number from known allocation patterns and thereby break the obfuscation scheme? To start we can note that the low N bits are directly exposed in the ID of anything that requires 2^N-byte alignment.

Quoted 4 lines

I think it's really a question of whether the high order bits can be derived.

And of course the random number only needs to be derived once per boot before it reveals the address of everything with an ID.

Tejun already proposed to split ID space and use different poisons for them.

Quoted 2 lines

Some wild speculation:

Quoted 8 lines
Quoted 5 lines

I bet you could use some cpu affinity, mem policy, slab info, mmap tricks, etc. to derive more low bits of the random number. You can probably get even more when you consider objects that don't fit evenly in slabs. Speaking of slabs, is there some way to use the fact that nearby slab objects will share their high ID bits?

OK, let's assume we found out that two mm_struct IDs have higher bits equal, what can we do next to split address bits from the poison ones?

Perhaps we can figure out where things are likely to be allocated by looking at the booted kernel in /boot.

Do we really need to spend time discussing precisely how this ID scheme can be attacked? I think we're better off just switching to the sha* hash scheme or 64-bit counter instead.

It would also be good to specify that the IDs presented to userspace are identifying strings (names) -- not identifying numbers. This way the ABI will be forward and backward compatible if the kernel ever needs to change the way it generates them.

Cheers,
-Matt