This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
arm_kernel_panic [2018/11/28 12:55] rpjday [Disassembling ''futex_wait_setup''] |
arm_kernel_panic [2018/11/28 13:02] rpjday [The offending(?) kernbel code] |
||
---|---|---|---|
Line 29: | Line 29: | ||
* register ''r5'' contains the value ''00000002'' | * register ''r5'' contains the value ''00000002'' | ||
+ | ===== The offending(?) kernel code ===== | ||
+ | |||
+ | From ''kernel/futex.c'': | ||
+ | |||
+ | <code> | ||
+ | /* The key must be already stored in q->key. */ | ||
+ | static inline struct futex_hash_bucket *queue_lock(struct futex_q *q) | ||
+ | __acquires(&hb->lock) | ||
+ | { | ||
+ | struct futex_hash_bucket *hb; | ||
+ | |||
+ | hb = hash_futex(&q->key); | ||
+ | |||
+ | /* | ||
+ | * Increment the counter before taking the lock so that | ||
+ | * a potential waker won't miss a to-be-slept task that is | ||
+ | * waiting for the spinlock. This is safe as all queue_lock() | ||
+ | * users end up calling queue_me(). Similarly, for housekeeping, | ||
+ | * decrement the counter at queue_unlock() when some error has | ||
+ | * occurred and we don't end up adding the task to the list. | ||
+ | */ | ||
+ | hb_waiters_inc(hb); | ||
+ | |||
+ | q->lock_ptr = &hb->lock; | ||
+ | |||
+ | spin_lock(&hb->lock); /* implies MB (A) */ | ||
+ | return hb; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code> | ||
+ | /** | ||
+ | * futex_wait_setup() - Prepare to wait on a futex | ||
+ | * @uaddr: the futex userspace address | ||
+ | * @val: the expected value | ||
+ | * @flags: futex flags (FLAGS_SHARED, etc.) | ||
+ | * @q: the associated futex_q | ||
+ | * @hb: storage for hash_bucket pointer to be returned to caller | ||
+ | * | ||
+ | * Setup the futex_q and locate the hash_bucket. Get the futex value and | ||
+ | * compare it with the expected value. Handle atomic faults internally. | ||
+ | * Return with the hb lock held and a q.key reference on success, and unlocked | ||
+ | * with no q.key reference on failure. | ||
+ | * | ||
+ | * Return: | ||
+ | * 0 - uaddr contains val and hb has been locked; | ||
+ | * <1 - -EFAULT or -EWOULDBLOCK (uaddr does not contain val) and hb is unlocked | ||
+ | */ | ||
+ | static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags, | ||
+ | struct futex_q *q, struct futex_hash_bucket **hb) | ||
+ | { | ||
+ | u32 uval; | ||
+ | int ret; | ||
+ | |||
+ | /* | ||
+ | * Access the page AFTER the hash-bucket is locked. | ||
+ | * Order is important: | ||
+ | * | ||
+ | * Userspace waiter: val = var; if (cond(val)) futex_wait(&var, val); | ||
+ | * Userspace waker: if (cond(var)) { var = new; futex_wake(&var); } | ||
+ | * | ||
+ | * The basic logical guarantee of a futex is that it blocks ONLY | ||
+ | * if cond(var) is known to be true at the time of blocking, for | ||
+ | * any cond. If we locked the hash-bucket after testing *uaddr, that | ||
+ | * would open a race condition where we could block indefinitely with | ||
+ | * cond(var) false, which would violate the guarantee. | ||
+ | * | ||
+ | * On the other hand, we insert q and release the hash-bucket only | ||
+ | * after testing *uaddr. This guarantees that futex_wait() will NOT | ||
+ | * absorb a wakeup if *uaddr does not match the desired values | ||
+ | * while the syscall executes. | ||
+ | */ | ||
+ | retry: | ||
+ | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, VERIFY_READ); | ||
+ | if (unlikely(ret != 0)) | ||
+ | return ret; | ||
+ | |||
+ | retry_private: | ||
+ | *hb = queue_lock(q); | ||
+ | |||
+ | ret = get_futex_value_locked(&uval, uaddr); | ||
+ | |||
+ | if (ret) { | ||
+ | queue_unlock(*hb); | ||
+ | |||
+ | ret = get_user(uval, uaddr); | ||
+ | if (ret) | ||
+ | goto out; | ||
+ | |||
+ | if (!(flags & FLAGS_SHARED)) | ||
+ | goto retry_private; | ||
+ | |||
+ | put_futex_key(&q->key); | ||
+ | goto retry; | ||
+ | } | ||
+ | |||
+ | if (uval != val) { | ||
+ | queue_unlock(*hb); | ||
+ | ret = -EWOULDBLOCK; | ||
+ | } | ||
+ | |||
+ | out: | ||
+ | if (ret) | ||
+ | put_futex_key(&q->key); | ||
+ | return ret; | ||
+ | } | ||
+ | </code> | ||
===== Disassembling ''futex_wait_setup'' ===== | ===== Disassembling ''futex_wait_setup'' ===== | ||