From fa36f31ad068e0498c61a1454598be8d8333ee05 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2004 00:26:00 -0800 Subject: [PATCH] Clean up dentry pointer validation by moving it into 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 | 13 ++----------- include/linux/slab.h | 1 + mm/slab.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 03825e26dc49d..95941d73cd201 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -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) diff --git a/include/linux/slab.h b/include/linux/slab.h index d797c981f37e9..69be5b308a118 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -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; diff --git a/mm/slab.c b/mm/slab.c index 155399a024813..744b70e4e563c 100644 --- 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. -- 2.53.0