]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/uprobes.c
drivers/rtc/rtc-wm831x.c: convert to devm_kzalloc()
[karo-tx-linux.git] / kernel / uprobes.c
index 3e7c4c51ad155ee24a1c48c9f47855ad69303eb0..249319118d367d60b15333bfb1eb4eec000da464 100644 (file)
@@ -436,6 +436,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe)
        spin_lock_irqsave(&uprobes_treelock, flags);
        u = __insert_uprobe(uprobe);
        spin_unlock_irqrestore(&uprobes_treelock, flags);
+
+       /* For now assume that the instruction need not be single-stepped */
+       uprobe->flags |= UPROBES_SKIP_SSTEP;
        return u;
 }
 
@@ -475,6 +478,9 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
 {
        struct uprobe_consumer *consumer;
 
+       if (!(uprobe->flags & UPROBES_RUN_HANDLER))
+               return;
+
        down_read(&uprobe->consumer_rwsem);
        consumer = uprobe->consumers;
        for (consumer = uprobe->consumers; consumer;
@@ -594,7 +600,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe,
                return -EEXIST;
 
        addr = (unsigned long)vaddr;
-       if (!uprobe->copy) {
+       if (!(uprobe->flags & UPROBES_COPY_INSN)) {
                ret = copy_insn(uprobe, vma, addr);
                if (ret)
                        return ret;
@@ -606,7 +612,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe,
                if (ret)
                        return ret;
 
-               uprobe->copy = 1;
+               uprobe->flags |= UPROBES_COPY_INSN;
        }
        ret = set_bkpt(mm, uprobe, addr);
        if (!ret)
@@ -850,7 +856,8 @@ int register_uprobe(struct inode *inode, loff_t offset,
                if (ret) {
                        uprobe->consumers = NULL;
                        __unregister_uprobe(inode, offset, uprobe);
-               }
+               } else
+                       uprobe->flags |= UPROBES_RUN_HANDLER;
        }
 
        mutex_unlock(uprobes_hash(inode));
@@ -886,9 +893,10 @@ void unregister_uprobe(struct inode *inode, loff_t offset,
                goto unreg_out;
        }
 
-       if (!uprobe->consumers)
+       if (!uprobe->consumers) {
                __unregister_uprobe(inode, offset, uprobe);
-
+               uprobe->flags &= ~UPROBES_RUN_HANDLER;
+       }
        mutex_unlock(uprobes_hash(inode));
 
 unreg_out:
@@ -1326,11 +1334,23 @@ bool uprobe_deny_signal(void)
                spin_lock_irq(&tsk->sighand->siglock);
                clear_tsk_thread_flag(tsk, TIF_SIGPENDING);
                spin_unlock_irq(&tsk->sighand->siglock);
+
+               if (__fatal_signal_pending(tsk) || xol_was_trapped(tsk)) {
+                       utask->state = UTASK_SSTEP_TRAPPED;
+                       set_tsk_thread_flag(tsk, TIF_UPROBE);
+                       set_tsk_thread_flag(tsk, TIF_NOTIFY_RESUME);
+               }
        }
 
        return true;
 }
 
+bool __weak can_skip_xol(struct pt_regs *regs, struct uprobe *u)
+{
+       u->flags &= ~UPROBES_SKIP_SSTEP;
+       return false;
+}
+
 /*
  * uprobe_notify_resume gets called in task context just before returning
  * to userspace.
@@ -1372,6 +1392,10 @@ void uprobe_notify_resume(struct pt_regs *regs)
                }
                utask->active_uprobe = u;
                handler_chain(u, regs);
+
+               if (u->flags & UPROBES_SKIP_SSTEP && can_skip_xol(regs, u))
+                       goto cleanup_ret;
+
                utask->state = UTASK_SSTEP;
                if (!pre_ssout(u, regs, probept))
                        user_enable_single_step(current);
@@ -1382,6 +1406,8 @@ void uprobe_notify_resume(struct pt_regs *regs)
                u = utask->active_uprobe;
                if (utask->state == UTASK_SSTEP_ACK)
                        post_xol(u, regs);
+               else if (utask->state == UTASK_SSTEP_TRAPPED)
+                       abort_xol(regs, u);
                else
                        WARN_ON_ONCE(1);
 
@@ -1403,11 +1429,12 @@ cleanup_ret:
                utask->state = UTASK_RUNNING;
        }
        if (u) {
+               if (!(u->flags & UPROBES_SKIP_SSTEP))
+                       set_instruction_pointer(regs, probept);
+
                put_uprobe(u);
-               set_instruction_pointer(regs, probept);
-       } else {
-               /*TODO Return SIGTRAP signal */
-       }
+       } else
+               send_sig(SIGTRAP, current, 0);
 }
 
 /*