]> git.neil.brown.name Git - history.git/commitdiff
Clean up dentry pointer validation by moving it into
authorLinus Torvalds <torvalds@home.osdl.org>
Mon, 9 Feb 2004 08:26:00 +0000 (00:26 -0800)
committerLinus Torvalds <torvalds@home.osdl.org>
Mon, 9 Feb 2004 08:26:00 +0000 (00:26 -0800)
a function of its own.

This also allows us to do a better job, since slab.c
can now do more proper tests.

fs/dcache.c
include/linux/slab.h
mm/slab.c

index 03825e26dc49ddf475e39a29f4e38f0a98ea7edc..95941d73cd20168ce5d95809bd7620480c49c816 100644 (file)
@@ -1035,20 +1035,11 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
  
 int d_validate(struct dentry *dentry, struct dentry *dparent)
 {
-       unsigned long dent_addr = (unsigned long) dentry;
-       unsigned long min_addr = PAGE_OFFSET;
-       unsigned long align_mask = 0x0F;
        struct hlist_head *base;
        struct hlist_node *lhp;
 
-       if (dent_addr < min_addr)
-               goto out;
-       if (dent_addr > (unsigned long)high_memory - sizeof(struct dentry))
-               goto out;
-       if (dent_addr & align_mask)
-               goto out;
-       if ((!kern_addr_valid(dent_addr)) || (!kern_addr_valid(dent_addr -1 +
-                                               sizeof(struct dentry))))
+       /* Check whether the ptr might be valid at all.. */
+       if (!kmem_ptr_validate(dentry_cache, dentry))
                goto out;
 
        if (dentry->d_parent != dparent)
index d797c981f37e99d48ae5acfe4c6e1de1772cd74f..69be5b308a1186cf53598baa62b6298db7591c25 100644 (file)
@@ -101,6 +101,7 @@ extern void kfree(const void *);
 extern unsigned int ksize(const void *);
 
 extern int FASTCALL(kmem_cache_reap(int));
+extern int FASTCALL(kmem_ptr_validate(kmem_cache_t *cachep, void *ptr));
 
 /* System wide caches */
 extern kmem_cache_t    *vm_area_cachep;
index 155399a024813f03c9f4c3729adebfc52003b550..744b70e4e563c39036e993552d40bdea201e9fc3 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2073,6 +2073,48 @@ void * kmem_cache_alloc (kmem_cache_t *cachep, int flags)
 
 EXPORT_SYMBOL(kmem_cache_alloc);
 
+/**
+ * kmem_ptr_validate - check if an untrusted pointer might
+ *     be a slab entry.
+ * @cachep: the cache we're checking against
+ * @ptr: pointer to validate
+ *
+ * This verifies that the untrusted pointer looks sane:
+ * it is _not_ a guarantee that the pointer is actually
+ * part of the slab cache in question, but it at least
+ * validates that the pointer can be dereferenced and
+ * looks half-way sane.
+ *
+ * Currently only used for dentry validation.
+ */
+int kmem_ptr_validate(kmem_cache_t *cachep, 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->objsize;
+       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)))
+               goto out;
+       page = virt_to_page(ptr);
+       if (unlikely(!PageSlab(page)))
+               goto out;
+       if (unlikely(GET_PAGE_CACHE(page) != cachep))
+               goto out;
+       return 1;
+out:
+       return 0;
+}
+
 /**
  * kmalloc - allocate memory
  * @size: how many bytes of memory are required.