Add poisoning to vm_area_struct to catch corruption at either the beginning or
the end of the struct.

Signed-off-by: Sasha Levin <sasha.le...@oracle.com>
---
 fs/exec.c                |  5 +++++
 include/linux/mm_types.h |  6 ++++++
 include/linux/mmdebug.h  |  6 ++++++
 kernel/fork.c            |  2 ++
 mm/debug.c               | 11 +++++++++--
 mm/mmap.c                | 19 ++++++++++++++++++-
 mm/nommu.c               |  7 +++++++
 mm/vmacache.c            |  3 +++
 8 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 7302b75..6cdd652 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -257,6 +257,10 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
                return -ENOMEM;
 
        down_write(&mm->mmap_sem);
+#ifdef CONFIG_DEBUG_VM_POISON
+       vma->poison_start = MM_POISON_BEGIN;
+       vma->poison_end = MM_POISON_END;
+#endif
        vma->vm_mm = mm;
 
        /*
@@ -283,6 +287,7 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
 err:
        up_write(&mm->mmap_sem);
        bprm->vma = NULL;
+       VM_CHECK_POISON_VMA(vma);
        kmem_cache_free(vm_area_cachep, vma);
        return err;
 }
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 0b0d324..4e2cf93 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -245,6 +245,9 @@ struct vm_region {
  * library, the executable area etc).
  */
 struct vm_area_struct {
+#ifdef CONFIG_DEBUG_VM_POISON
+       u32 poison_start;
+#endif
        /* The first cache line has the info for VMA tree walking. */
 
        unsigned long vm_start;         /* Our start address within vm_mm. */
@@ -308,6 +311,9 @@ struct vm_area_struct {
 #ifdef CONFIG_NUMA
        struct mempolicy *vm_policy;    /* NUMA policy for the VMA */
 #endif
+#ifdef CONFIG_DEBUG_VM_POISON
+       u32 poison_end;
+#endif
 };
 
 struct core_thread {
diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h
index 339e40f..75bc69d 100644
--- a/include/linux/mmdebug.h
+++ b/include/linux/mmdebug.h
@@ -45,6 +45,11 @@ void dump_mm(const struct mm_struct *mm);
                VM_BUG_ON_MM((mm)->poison_start != MM_POISON_BEGIN, (mm));\
                VM_BUG_ON_MM((mm)->poison_end != MM_POISON_END, (mm));  \
        } while (0)
+#define VM_CHECK_POISON_VMA(vma)                                       \
+       do {                                                            \
+               VM_BUG_ON_VMA((vma)->poison_start != MM_POISON_BEGIN, (vma));\
+               VM_BUG_ON_VMA((vma)->poison_end != MM_POISON_END, (vma));\
+       } while (0)
 #endif
 #else
 #define VM_BUG_ON(cond) BUILD_BUG_ON_INVALID(cond)
@@ -55,6 +60,7 @@ void dump_mm(const struct mm_struct *mm);
 #define VM_WARN_ON_ONCE(cond) BUILD_BUG_ON_INVALID(cond)
 #define VM_WARN_ONCE(cond, format...) BUILD_BUG_ON_INVALID(cond)
 #define VM_CHECK_POISON_MM(mm) do { } while(0)
+#define VM_CHECK_POISON_VMA(vma) do { } while(0)
 #endif
 
 #ifdef CONFIG_DEBUG_VIRTUAL
diff --git a/kernel/fork.c b/kernel/fork.c
index 26bedfa..c3ae913 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -415,6 +415,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct 
*oldmm)
                tmp = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
                if (!tmp)
                        goto fail_nomem;
+               VM_CHECK_POISON_VMA(mpnt);
                *tmp = *mpnt;
                INIT_LIST_HEAD(&tmp->anon_vma_chain);
                retval = vma_dup_policy(mpnt, tmp);
@@ -489,6 +490,7 @@ out:
 fail_nomem_anon_vma_fork:
        mpol_put(vma_policy(tmp));
 fail_nomem_policy:
+       VM_CHECK_POISON_VMA(tmp);
        kmem_cache_free(vm_area_cachep, tmp);
 fail_nomem:
        retval = -ENOMEM;
diff --git a/mm/debug.c b/mm/debug.c
index a1ebc5e..d53174e 100644
--- a/mm/debug.c
+++ b/mm/debug.c
@@ -152,11 +152,18 @@ static const struct trace_print_flags vmaflags_names[] = {
 void dump_vma(const struct vm_area_struct *vma)
 {
        pr_emerg("vma %p start %p end %p\n"
+#ifdef CONFIG_DEBUG_VM_POISON
+               "start poison: %s end poison: %s\n"
+#endif
                "next %p prev %p mm %p\n"
                "prot %lx anon_vma %p vm_ops %p\n"
                "pgoff %lx file %p private_data %p\n",
-               vma, (void *)vma->vm_start, (void *)vma->vm_end, vma->vm_next,
-               vma->vm_prev, vma->vm_mm,
+               vma, (void *)vma->vm_start, (void *)vma->vm_end,
+#ifdef CONFIG_DEBUG_VM_POISON
+               (vma->poison_start == MM_POISON_BEGIN) ? "valid" : "invalid",
+               (vma->poison_end == MM_POISON_END) ? "valid" : "invalid",
+#endif
+               vma->vm_next, vma->vm_prev, vma->vm_mm,
                (unsigned long)pgprot_val(vma->vm_page_prot),
                vma->anon_vma, vma->vm_ops, vma->vm_pgoff,
                vma->vm_file, vma->vm_private_data);
diff --git a/mm/mmap.c b/mm/mmap.c
index 3240bbc..da5ffeb 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -274,6 +274,7 @@ static struct vm_area_struct *remove_vma(struct 
vm_area_struct *vma)
        struct vm_area_struct *next = vma->vm_next;
 
        might_sleep();
+       VM_CHECK_POISON_VMA(vma);
        if (vma->vm_ops && vma->vm_ops->close)
                vma->vm_ops->close(vma);
        if (vma->vm_file)
@@ -900,6 +901,7 @@ again:                      remove_next = 1 + (end > 
next->vm_end);
                        anon_vma_merge(vma, next);
                mm->map_count--;
                mpol_put(vma_policy(next));
+               VM_CHECK_POISON_VMA(next);
                kmem_cache_free(vm_area_cachep, next);
                /*
                 * In mprotect's case 6 (see comments on vma_merge),
@@ -1594,6 +1596,10 @@ munmap_back:
                goto unacct_error;
        }
 
+#ifdef CONFIG_DEBUG_VM_POISON
+       vma->poison_start = MM_POISON_BEGIN;
+       vma->poison_end = MM_POISON_END;
+#endif
        vma->vm_mm = mm;
        vma->vm_start = addr;
        vma->vm_end = addr + len;
@@ -1691,6 +1697,7 @@ allow_write_and_free_vma:
        if (vm_flags & VM_DENYWRITE)
                allow_write_access(file);
 free_vma:
+       VM_CHECK_POISON_VMA(vma);
        kmem_cache_free(vm_area_cachep, vma);
 unacct_error:
        if (charged)
@@ -2454,7 +2461,7 @@ static int __split_vma(struct mm_struct *mm, struct 
vm_area_struct *vma,
        new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
        if (!new)
                goto out_err;
-
+       VM_CHECK_POISON_VMA(vma);
        /* most fields are the same, copy all, and then fixup */
        *new = *vma;
 
@@ -2499,6 +2506,7 @@ static int __split_vma(struct mm_struct *mm, struct 
vm_area_struct *vma,
  out_free_mpol:
        mpol_put(vma_policy(new));
  out_free_vma:
+       VM_CHECK_POISON_VMA(new);
        kmem_cache_free(vm_area_cachep, new);
  out_err:
        return err;
@@ -2771,6 +2779,10 @@ static unsigned long do_brk(unsigned long addr, unsigned 
long len)
                return -ENOMEM;
        }
 
+#ifdef CONFIG_DEBUG_VM_POISON
+       vma->poison_start = MM_POISON_BEGIN;
+       vma->poison_end = MM_POISON_END;
+#endif
        INIT_LIST_HEAD(&vma->anon_vma_chain);
        vma->vm_mm = mm;
        vma->vm_start = addr;
@@ -2942,6 +2954,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct 
**vmap,
                *need_rmap_locks = (new_vma->vm_pgoff <= vma->vm_pgoff);
        } else {
                new_vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
+               VM_CHECK_POISON_VMA(vma);
                if (new_vma) {
                        *new_vma = *vma;
                        new_vma->vm_start = addr;
@@ -3058,6 +3071,10 @@ static struct vm_area_struct *__install_special_mapping(
                return ERR_PTR(-ENOMEM);
 
        INIT_LIST_HEAD(&vma->anon_vma_chain);
+#ifdef CONFIG_DEBUG_VM_POISON
+       vma->poison_start = MM_POISON_BEGIN;
+       vma->poison_end = MM_POISON_END;
+#endif
        vma->vm_mm = mm;
        vma->vm_start = addr;
        vma->vm_end = addr + len;
diff --git a/mm/nommu.c b/mm/nommu.c
index bd10aa1..e81b656 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -820,6 +820,7 @@ static void delete_vma_from_mm(struct vm_area_struct *vma)
 static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma)
 {
        kenter("%p", vma);
+       VM_CHECK_POISON_VMA(vma);
        if (vma->vm_ops && vma->vm_ops->close)
                vma->vm_ops->close(vma);
        if (vma->vm_file)
@@ -1302,6 +1303,10 @@ unsigned long do_mmap_pgoff(struct file *file,
        if (!vma)
                goto error_getting_vma;
 
+#ifdef CONFIG_DEBUG_VM_POISON
+       vma->poison_start = MM_POISON_BEGIN;
+       vma->poison_end = MM_POISON_END;
+#endif
        region->vm_usage = 1;
        region->vm_flags = vm_flags;
        region->vm_pgoff = pgoff;
@@ -1465,6 +1470,7 @@ error:
        kmem_cache_free(vm_region_jar, region);
        if (vma->vm_file)
                fput(vma->vm_file);
+       VM_CHECK_POISON_VMA(vma);
        kmem_cache_free(vm_area_cachep, vma);
        kleave(" = %d", ret);
        return ret;
@@ -1571,6 +1577,7 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct 
*vma,
        }
 
        /* most fields are the same, copy all, and then fixup */
+       VM_CHECK_POISON_VMA(vma);
        *new = *vma;
        *region = *vma->vm_region;
        new->vm_region = region;
diff --git a/mm/vmacache.c b/mm/vmacache.c
index d507caa..27760cf 100644
--- a/mm/vmacache.c
+++ b/mm/vmacache.c
@@ -59,6 +59,7 @@ static bool vmacache_valid_mm(struct mm_struct *mm)
 
 void vmacache_update(unsigned long addr, struct vm_area_struct *newvma)
 {
+       VM_CHECK_POISON_VMA(newvma);
        if (vmacache_valid_mm(newvma->vm_mm))
                current->vmacache[VMACACHE_HASH(addr)] = newvma;
 }
@@ -99,6 +100,7 @@ struct vm_area_struct *vmacache_find(struct mm_struct *mm, 
unsigned long addr)
                        continue;
                if (WARN_ON_ONCE(vma->vm_mm != mm))
                        break;
+               VM_CHECK_POISON_VMA(vma);
                if (vma->vm_start <= addr && vma->vm_end > addr) {
                        count_vm_vmacache_event(VMACACHE_FIND_HITS);
                        return vma;
@@ -123,6 +125,7 @@ struct vm_area_struct *vmacache_find_exact(struct mm_struct 
*mm,
        for (i = 0; i < VMACACHE_SIZE; i++) {
                struct vm_area_struct *vma = current->vmacache[i];
 
+               VM_CHECK_POISON_VMA(vma);
                if (vma && vma->vm_start == start && vma->vm_end == end) {
                        count_vm_vmacache_event(VMACACHE_FIND_HITS);
                        return vma;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to