User Tools

Site Tools


arm_kernel_panic

Overview

An example of debugging a kernel panic by disassembling an ARM Linux kernel.

The kernel panic

[  128.459816] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[  128.468018] Unable to handle kernel NULL pointer dereference at virtual address 00000002
[  128.468026] pgd = d8f38000
[  128.468035] [00000002] *pgd=1a660831, *pte=00000000, *ppte=00000000
[  128.468041] Internal error: Oops: 817 [#1] PREEMPT SMP ARM
[  128.468046] Modules linked in:
[  128.468062] task: d8f29300 ti: d8f70000 task.ti: d8f70000
[  128.468074] PC is at futex_wait_setup+0x90/0x14c
[  128.468080] LR is at get_parent_ip+0x10/0x2c
[  128.468086] pc : [<c00704d8>]    lr : [<c0040f30>]    psr: 600d0013
[  128.468086] sp : d8f71df8  ip : f66b1657  fp : 00000000
[  128.468090] r10: 00000002  r9 : 00000000  r8 : d8f71e88
[  128.468094] r7 : d8f70000  r6 : b56b1654  r5 : 00000002  r4 : d8f71ea4
[  128.468099] r3 : d96c4240  r2 : 000002ce  r1 : b56b1654  r0 : d8f71e04
[  128.468104] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
[  128.468109] Control: 10c5387d  Table: 18f3806a  DAC: 00000015

As you can see:

  • kernel panic occurred in futex_wait_setup, offset 0x90
  • register r5 contains the value 00000002

The offending(?) kernel code

From kernel/futex.c:

/* 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;
}
/**
 * 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;
}

Disassembling ''futex_wait_setup''

Using:

$ arm-wrs-linux-gnueabi-objdump -D -m arm vmlinux
c0070448 <futex_wait_setup>:
c0070448:       e92d4ff0        push    {r4, r5, r6, r7, r8, r9, sl, fp, lr}
c007044c:       e24dd014        sub     sp, sp, #20
c0070450:       e1a08003        mov     r8, r3
c0070454:       e1a06000        mov     r6, r0
c0070458:       e1a0300d        mov     r3, sp
c007045c:       e59d5038        ldr     r5, [sp, #56]   ; 0x38
c0070460:       e3c37d7f        bic     r7, r3, #8128   ; 0x1fc0
c0070464:       e1a0a001        mov     sl, r1
c0070468:       e3c7703f        bic     r7, r7, #63     ; 0x3f
c007046c:       e1a0b002        mov     fp, r2
c0070470:       e288401c        add     r4, r8, #28
c0070474:       e20b9001        and     r9, fp, #1
c0070478:       e3a03000        mov     r3, #0
c007047c:       e1a00006        mov     r0, r6
c0070480:       e1a02004        mov     r2, r4
c0070484:       e1a01009        mov     r1, r9
c0070488:       ebfffd3f        bl      c006f98c <get_futex_key>
c007048c:       e3500000        cmp     r0, #0
c0070490:       1a00003d        bne     c007058c <futex_wait_setup+0x144>
c0070494:       e1a00004        mov     r0, r4
c0070498:       ebfffc7d        bl      c006f694 <hash_futex>
c007049c:       e1a03000        mov     r3, r0
c00704a0:       f590f000        pldw    [r0]
c00704a4:       e1902f9f        ldrex   r2, [r0]
c00704a8:       e2822001        add     r2, r2, #1
c00704ac:       e1801f92        strex   r1, r2, [r0]
c00704b0:       e3310000        teq     r1, #0
c00704b4:       1afffffa        bne     c00704a4 <futex_wait_setup+0x5c>
c00704b8:       f57ff05b        dmb     ish
c00704bc:       e2800004        add     r0, r0, #4
c00704c0:       e5880018        str     r0, [r8, #24]
c00704c4:       e58d3004        str     r3, [sp, #4]
c00704c8:       eb08c1b2        bl      c02a0b98 <_raw_spin_lock>
c00704cc:       e1a01006        mov     r1, r6
c00704d0:       e59d3004        ldr     r3, [sp, #4]
c00704d4:       e28d000c        add     r0, sp, #12
c00704d8:       e5853000        str     r3, [r5]
c00704dc:       ebfffdea        bl      c006fc8c <get_futex_value_locked>
c00704e0:       e3500000        cmp     r0, #0
c00704e4:       0a000016        beq     c0070544 <futex_wait_setup+0xfc>
c00704e8:       e5953000        ldr     r3, [r5]
c00704ec:       e2830004        add     r0, r3, #4
c00704f0:       e58d3004        str     r3, [sp, #4]
c00704f4:       eb08c243        bl      c02a0e08 <_raw_spin_unlock>
c00704f8:       e59d3004        ldr     r3, [sp, #4]
c00704fc:       f593f000        pldw    [r3]
c0070500:       e1932f9f        ldrex   r2, [r3]
c0070504:       e2422001        sub     r2, r2, #1
c0070508:       e1831f92        strex   r1, r2, [r3]
c007050c:       e3310000        teq     r1, #0
c0070510:       1afffffa        bne     c0070500 <futex_wait_setup+0xb8>
c0070514:       e5971008        ldr     r1, [r7, #8]
c0070518:       e1a00006        mov     r0, r6
c007051c:       e2411001        sub     r1, r1, #1
c0070520:       eb0447df        bl      c01824a4 <__get_user_4>
c0070524:       e2503000        subs    r3, r0, #0
c0070528:       e58d200c        str     r2, [sp, #12]
c007052c:       1a000011        bne     c0070578 <futex_wait_setup+0x130>
c0070530:       e3590000        cmp     r9, #0
c0070534:       0affffd6        beq     c0070494 <futex_wait_setup+0x4c>
c0070538:       e1a00004        mov     r0, r4
c007053c:       ebffff52        bl      c007028c <drop_futex_key_refs>
c0070540:       eaffffca        b       c0070470 <futex_wait_setup+0x28>
c0070544:       e59d300c        ldr     r3, [sp, #12]
c0070548:       e153000a        cmp     r3, sl
c007054c:       0a00000e        beq     c007058c <futex_wait_setup+0x144>
c0070550:       e5955000        ldr     r5, [r5]
c0070554:       e2850004        add     r0, r5, #4
c0070558:       eb08c22a        bl      c02a0e08 <_raw_spin_unlock>
c007055c:       f595f000        pldw    [r5]
c0070560:       e1953f9f        ldrex   r3, [r5]
c0070564:       e2433001        sub     r3, r3, #1
c0070568:       e1852f93        strex   r2, r3, [r5]
c007056c:       e3320000        teq     r2, #0
c0070570:       1afffffa        bne     c0070560 <futex_wait_setup+0x118>
c0070574:       e3e0300a        mvn     r3, #10
c0070578:       e1a00004        mov     r0, r4
c007057c:       e58d3004        str     r3, [sp, #4]
c0070580:       ebffff41        bl      c007028c <drop_futex_key_refs>
c0070584:       e59d3004        ldr     r3, [sp, #4]
c0070588:       e1a00003        mov     r0, r3
c007058c:       e28dd014        add     sp, sp, #20
c0070590:       e8bd8ff0        pop     {r4, r5, r6, r7, r8, r9, sl, fp, pc}
arm_kernel_panic.txt · Last modified: 2018/11/28 13:02 by rpjday