]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - kernel/futex.c
futex: Fault/error injection capabilities
[karo-tx-linux.git] / kernel / futex.c
index 153eb22b0fc07bdd784da3031c6e45fb27b5623d..6ea31bb703c95c209db9dee42a5ab4f943ef018a 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/hugetlb.h>
 #include <linux/freezer.h>
 #include <linux/bootmem.h>
+#include <linux/fault-inject.h>
 
 #include <asm/futex.h>
 
@@ -258,6 +259,66 @@ static unsigned long __read_mostly futex_hashsize;
 
 static struct futex_hash_bucket *futex_queues;
 
+/*
+ * Fault injections for futexes.
+ */
+#ifdef CONFIG_FAIL_FUTEX
+
+static struct {
+       struct fault_attr attr;
+
+       u32 ignore_private;
+} fail_futex = {
+       .attr = FAULT_ATTR_INITIALIZER,
+       .ignore_private = 0,
+};
+
+static int __init setup_fail_futex(char *str)
+{
+       return setup_fault_attr(&fail_futex.attr, str);
+}
+__setup("fail_futex=", setup_fail_futex);
+
+bool should_fail_futex(bool fshared)
+{
+       if (fail_futex.ignore_private && !fshared)
+               return false;
+
+       return should_fail(&fail_futex.attr, 1);
+}
+
+#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
+
+static int __init fail_futex_debugfs(void)
+{
+       umode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
+       struct dentry *dir;
+
+       dir = fault_create_debugfs_attr("fail_futex", NULL,
+                                       &fail_futex.attr);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+
+       if (!debugfs_create_bool("ignore-private", mode, dir,
+                                &fail_futex.ignore_private)) {
+               debugfs_remove_recursive(dir);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+late_initcall(fail_futex_debugfs);
+
+#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
+
+#else
+static inline bool should_fail_futex(bool fshared)
+{
+       return false;
+}
+#endif /* CONFIG_FAIL_FUTEX */
+
 static inline void futex_get_mm(union futex_key *key)
 {
        atomic_inc(&key->private.mm->mm_count);
@@ -413,6 +474,9 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)
        if (unlikely(!access_ok(rw, uaddr, sizeof(u32))))
                return -EFAULT;
 
+       if (unlikely(should_fail_futex(fshared)))
+               return -EFAULT;
+
        /*
         * PROCESS_PRIVATE futexes are fast.
         * As the mm cannot disappear under us and the 'key' only needs
@@ -428,6 +492,10 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)
        }
 
 again:
+       /* Ignore any VERIFY_READ mapping (futex common case) */
+       if (unlikely(should_fail_futex(fshared)))
+               return -EFAULT;
+
        err = get_user_pages_fast(address, 1, 1, &page);
        /*
         * If write access is not required (eg. FUTEX_WAIT), try
@@ -516,7 +584,7 @@ again:
                 * A RO anonymous page will never change and thus doesn't make
                 * sense for futex operations.
                 */
-               if (ro) {
+               if (unlikely(should_fail_futex(fshared)) || ro) {
                        err = -EFAULT;
                        goto out;
                }
@@ -974,6 +1042,9 @@ static int lock_pi_update_atomic(u32 __user *uaddr, u32 uval, u32 newval)
 {
        u32 uninitialized_var(curval);
 
+       if (unlikely(should_fail_futex(true)))
+               return -EFAULT;
+
        if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)))
                return -EFAULT;
 
@@ -1015,12 +1086,18 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
        if (get_futex_value_locked(&uval, uaddr))
                return -EFAULT;
 
+       if (unlikely(should_fail_futex(true)))
+               return -EFAULT;
+
        /*
         * Detect deadlocks.
         */
        if ((unlikely((uval & FUTEX_TID_MASK) == vpid)))
                return -EDEADLK;
 
+       if ((unlikely(should_fail_futex(true))))
+               return -EDEADLK;
+
        /*
         * Lookup existing state first. If it exists, try to attach to
         * its pi_state.
@@ -1155,6 +1232,9 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
         */
        newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
 
+       if (unlikely(should_fail_futex(true)))
+               ret = -EFAULT;
+
        if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
                ret = -EFAULT;
        else if (curval != uval)
@@ -1457,6 +1537,9 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex,
        if (get_futex_value_locked(&curval, pifutex))
                return -EFAULT;
 
+       if (unlikely(should_fail_futex(true)))
+               return -EFAULT;
+
        /*
         * Find the top_waiter and determine if there are additional waiters.
         * If the caller intends to requeue more than 1 waiter to pifutex,
@@ -2537,7 +2620,7 @@ int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb,
  * futex_wait_requeue_pi() - Wait on uaddr and take uaddr2
  * @uaddr:     the futex we initially wait on (non-pi)
  * @flags:     futex flags (FLAGS_SHARED, FLAGS_CLOCKRT, etc.), they must be
- *             the same type, no requeueing from private to shared, etc.
+ *             the same type, no requeueing from private to shared, etc.
  * @val:       the expected value of uaddr
  * @abs_time:  absolute timeout
  * @bitset:    32 bit wakeup bitset set by userspace, defaults to all
@@ -3012,6 +3095,8 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
        if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
                      cmd == FUTEX_WAIT_BITSET ||
                      cmd == FUTEX_WAIT_REQUEUE_PI)) {
+               if (unlikely(should_fail_futex(!(op & FUTEX_PRIVATE_FLAG))))
+                       return -EFAULT;
                if (copy_from_user(&ts, utime, sizeof(ts)) != 0)
                        return -EFAULT;
                if (!timespec_valid(&ts))