2010-04-07 16:29:55

by Pekka Enberg

[permalink] [raw]
Subject: [PATCH 1/2] slab: Generify kernel pointer validation

As suggested by Linus, introduce a kern_ptr_validate() helper that does some
sanity checks to make sure a pointer is a valid kernel pointer. This is a
preparational step for fixing SLUB kmem_ptr_validate().

Cc: Andrew Morton <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: David Rientjes <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Matt Mackall <[email protected]>
Cc: Nick Piggin <[email protected]>
Signed-off-by: Pekka Enberg <[email protected]>
---
include/linux/slab.h | 1 +
mm/slab.c | 13 +------------
mm/util.c | 21 +++++++++++++++++++++
3 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/include/linux/slab.h b/include/linux/slab.h
index 4884462..49d1247 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -106,6 +106,7 @@ int kmem_cache_shrink(struct kmem_cache *);
void kmem_cache_free(struct kmem_cache *, void *);
unsigned int kmem_cache_size(struct kmem_cache *);
const char *kmem_cache_name(struct kmem_cache *);
+int kern_ptr_validate(const void *ptr, unsigned long size);
int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr);

/*
diff --git a/mm/slab.c b/mm/slab.c
index a9f325b..bac0f4f 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -3602,21 +3602,10 @@ EXPORT_SYMBOL(kmem_cache_alloc_notrace);
*/
int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr)
{
- unsigned long addr = (unsigned long)ptr;
- unsigned long min_addr = PAGE_OFFSET;
- unsigned long align_mask = BYTES_PER_WORD - 1;
unsigned long size = cachep->buffer_size;
struct page *page;

- if (unlikely(addr < min_addr))
- goto out;
- if (unlikely(addr > (unsigned long)high_memory - size))
- goto out;
- if (unlikely(addr & align_mask))
- goto out;
- if (unlikely(!kern_addr_valid(addr)))
- goto out;
- if (unlikely(!kern_addr_valid(addr + size - 1)))
+ if (unlikely(!kern_ptr_validate(ptr, size)))
goto out;
page = virt_to_page(ptr);
if (unlikely(!PageSlab(page)))
diff --git a/mm/util.c b/mm/util.c
index 834db7b..f5712e8 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -186,6 +186,27 @@ void kzfree(const void *p)
}
EXPORT_SYMBOL(kzfree);

+int kern_ptr_validate(const void *ptr, unsigned long size)
+{
+ unsigned long addr = (unsigned long)ptr;
+ unsigned long min_addr = PAGE_OFFSET;
+ unsigned long align_mask = sizeof(void *) - 1;
+
+ if (unlikely(addr < min_addr))
+ goto out;
+ if (unlikely(addr > (unsigned long)high_memory - size))
+ goto out;
+ if (unlikely(addr & align_mask))
+ goto out;
+ if (unlikely(!kern_addr_valid(addr)))
+ goto out;
+ if (unlikely(!kern_addr_valid(addr + size - 1)))
+ goto out;
+ return 1;
+out:
+ return 0;
+}
+
/*
* strndup_user - duplicate an existing string from user space
* @s: The string to duplicate
--
1.6.3.3


2010-04-07 16:34:37

by Pekka Enberg

[permalink] [raw]
Subject: [PATCH 2/2] slub: Fix kmem_ptr_validate() for non-kernel pointers

As suggested by Linus, fix up kmem_ptr_validate() to handle non-kernel pointers
more graciously. The patch changes kmem_ptr_validate() to use the newly
introduced kern_ptr_validate() helper to check that a pointer is a valid kernel
pointer before we attempt to convert it into a 'struct page'.

Cc: Andrew Morton <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: David Rientjes <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Matt Mackall <[email protected]>
Cc: Nick Piggin <[email protected]>
Signed-off-by: Pekka Enberg <[email protected]>
---
mm/slub.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index b364844..7d6c8b1 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2386,6 +2386,9 @@ int kmem_ptr_validate(struct kmem_cache *s, const void *object)
{
struct page *page;

+ if (!kern_ptr_validate(object, s->size))
+ return 0;
+
page = get_object_page(object);

if (!page || s != page->slab)
--
1.6.3.3

2010-04-07 16:56:10

by Christoph Lameter

[permalink] [raw]
Subject: Re: [PATCH 1/2] slab: Generify kernel pointer validation

On Wed, 7 Apr 2010, Pekka Enberg wrote:

> +int kern_ptr_validate(const void *ptr, unsigned long size)
> +{
> + unsigned long addr = (unsigned long)ptr;
> + unsigned long min_addr = PAGE_OFFSET;

Use the min_addr from the security subsystem?

2010-04-08 18:59:55

by David Rientjes

[permalink] [raw]
Subject: Re: [PATCH 2/2] slub: Fix kmem_ptr_validate() for non-kernel pointers

On Wed, 7 Apr 2010, Pekka Enberg wrote:

> As suggested by Linus, fix up kmem_ptr_validate() to handle non-kernel pointers
> more graciously. The patch changes kmem_ptr_validate() to use the newly
> introduced kern_ptr_validate() helper to check that a pointer is a valid kernel
> pointer before we attempt to convert it into a 'struct page'.
>
> Cc: Andrew Morton <[email protected]>
> Cc: Christoph Lameter <[email protected]>
> Cc: David Rientjes <[email protected]>

Acked-by: David Rientjes <[email protected]>

> Cc: Ingo Molnar <[email protected]>
> Cc: Matt Mackall <[email protected]>
> Cc: Nick Piggin <[email protected]>
> Signed-off-by: Pekka Enberg <[email protected]>
> ---
> mm/slub.c | 3 +++
> 1 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/mm/slub.c b/mm/slub.c
> index b364844..7d6c8b1 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -2386,6 +2386,9 @@ int kmem_ptr_validate(struct kmem_cache *s, const void *object)
> {
> struct page *page;
>
> + if (!kern_ptr_validate(object, s->size))
> + return 0;
> +
> page = get_object_page(object);
>
> if (!page || s != page->slab)