]> git.openfabrics.org - ~shefty/rdma-dev.git/blobdiff - kernel/events/uprobes.c
uprobes: Use percpu_rw_semaphore to fix register/unregister vs dup_mmap() race
[~shefty/rdma-dev.git] / kernel / events / uprobes.c
index 5ce99cfd2e6e9b8259a660c347c5b78c9b0c2493..dea7acfbb0710889bf19e5fcb6584da4c5302f5d 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/ptrace.h>      /* user_enable_single_step */
 #include <linux/kdebug.h>      /* notifier mechanism */
 #include "../../mm/internal.h" /* munlock_vma_page */
+#include <linux/percpu-rwsem.h>
 
 #include <linux/uprobes.h>
 
@@ -71,6 +72,8 @@ static struct mutex uprobes_mutex[UPROBES_HASH_SZ];
 static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
 #define uprobes_mmap_hash(v)   (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ])
 
+static struct percpu_rw_semaphore dup_mmap_sem;
+
 /*
  * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe
  * events active at this time.  Probably a fine grained per inode count is
@@ -766,10 +769,13 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
        struct map_info *info;
        int err = 0;
 
+       percpu_down_write(&dup_mmap_sem);
        info = build_map_info(uprobe->inode->i_mapping,
                                        uprobe->offset, is_register);
-       if (IS_ERR(info))
-               return PTR_ERR(info);
+       if (IS_ERR(info)) {
+               err = PTR_ERR(info);
+               goto out;
+       }
 
        while (info) {
                struct mm_struct *mm = info->mm;
@@ -799,7 +805,8 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
                mmput(mm);
                info = free_map_info(info);
        }
-
+ out:
+       percpu_up_write(&dup_mmap_sem);
        return err;
 }
 
@@ -1131,6 +1138,16 @@ void uprobe_clear_state(struct mm_struct *mm)
        kfree(area);
 }
 
+void uprobe_start_dup_mmap(void)
+{
+       percpu_down_read(&dup_mmap_sem);
+}
+
+void uprobe_end_dup_mmap(void)
+{
+       percpu_up_read(&dup_mmap_sem);
+}
+
 void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
 {
        newmm->uprobes_state.xol_area = NULL;
@@ -1597,6 +1614,9 @@ static int __init init_uprobes(void)
                mutex_init(&uprobes_mmap_mutex[i]);
        }
 
+       if (percpu_init_rwsem(&dup_mmap_sem))
+               return -ENOMEM;
+
        return register_die_notifier(&uprobe_exception_nb);
 }
 module_init(init_uprobes);