lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <CA+pfE0ofj9nsUNmM7tOC8OZTJMC5pyM=Nu6LcFVbKTvos2fg7A@mail.gmail.com>
Date:	Wed, 1 Jun 2016 21:24:03 +0800
From:	张宁 <zhangning.dlut@...il.com>
To:	linux-kernel@...r.kernel.org
Cc:	chris.zn@...baba-inc.com
Subject: A "NULL pointer dereference " problem in pick_next_task_fair in linux
 3.10.0 based

hi everybody:

I have encountered a "NULL pointer dereference" problem in
pick_next_task_fair()  in linux 3.10.0 based

static struct task_struct *pick_next_task_fair(struct rq *rq)
{
    struct task_struct *p;
    struct cfs_rq *cfs_rq = &rq->cfs;
    struct sched_entity *se;

    if (!cfs_rq->nr_running)
        return NULL;

    do {
        se = pick_next_entity(cfs_rq);
        set_next_entity(cfs_rq, se);
        cfs_rq = group_cfs_rq(se);
    } while (cfs_rq);

    p = task_of(se);
    if (hrtick_enabled(rq))
        hrtick_start_fair(rq, p);

    return p;
}

In pick_next_entity():

static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq)
{
    struct sched_entity *se = __pick_first_entity(cfs_rq);
    struct sched_entity *left = se;

    /*
     * Avoid running the skip buddy, if running something else can
     * be done without getting too unfair.
     */
    if (cfs_rq->skip == se) {
        struct sched_entity *second = __pick_next_entity(se);
        if (second && wakeup_preempt_entity(second, left) < 1)
            se = second;
    }

    /*
     * Prefer last buddy, try to return the CPU to a preempted task.
     */
    if (cfs_rq->last && wakeup_preempt_entity(cfs_rq->last, left) < 1)
        se = cfs_rq->last;

    /*
     * Someone really wants this to run. If it's not unfair, run it.
     */
    if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1)
        se = cfs_rq->next;

    clear_buddies(cfs_rq, se);

    return se;
}


in pick_next_entity

 1. struct sched_entity *se = __pick_first_entity(cfs_rq);
    This se may be get NULL.

2. Then if cfs_rq->skip is also null, cfs_rq->skip == se.

3. struct sched_entity *second = __pick_next_entity(se);
    This code will lead to the "NULL pointer dereference" because se
is NULL in __pick_next_entity -> rb_next()

The oops is as follow:

[795797.511960] BUG: unable to handle kernel NULL pointer dereference
at 0000000000000010


[795797.622950] task: ffff883f24924560 ti: ffff883f2495c000 task.ti:
ffff883f2495c000

[795797.630541] RIP: 0010:[]  [] rb_next+0x1/0x50

[795797.638153] RSP: 0018:ffff883f2495fdc0  EFLAGS: 00010046

[795797.643575] RAX: 0000000000000000 RBX: 0000000000000000 RCX:
0000000000000000

[795797.650824] RDX: 0000000000000001 RSI: ffff883f7f5f4868 RDI:
0000000000000010

[795797.658068] RBP: ffff883f2495fe08 R08: 0000000000000000 R09:
0000000000000000

[795797.666476] R10: 0000000000000000 R11: ffff883f249f5400 R12:
ffff883d82207200

[795797.674821] R13: 0000000000000000 R14: 0000000000000000 R15:
0000000000000000

[795797.683160] FS:  0000000000000000(0000) GS:ffff883f7f5e0000(0000)
knlGS:0000000000000000

[795797.692460] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033

[795797.699395] CR2: 0000000000000010 CR3: 0000003f1de58000 CR4:
00000000003407e0

[795797.707703] DR0: 0000000000000000 DR1: 0000000000000000 DR2:
0000000000000000

[795797.715992] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7:
0000000000000400

[795797.724267] Stack:

[795797.727416]  ffff883f2495fe08 ffffffff810b87f9 ffff883f2495fe08
ffff883f7f5f47c0

[795797.736040]  ffff883f24924b40 ffff883f7f5f47c0 000000000000003f
ffff883f24924560

[795797.744658]  0000000000000000 ffff883f2495fe68 ffffffff816324ea
ffff883f24924560

[795797.753255] Call Trace:

[795797.756818]  [] ? pick_next_task_fair+0x129/0x1d0

[795797.764207]  [] __schedule+0x12a/0x910

[795797.770622]  [] schedule+0x29/0x70

[795797.776683]  [] smpboot_thread_fn+0xd3/0x1a0

[795797.783599]  [] ? schedule+0x29/0x70

[795797.789807]  [] ? lg_double_unlock+0x90/0x90

[795797.796706]  [] kthread+0xcf/0xe0

[795797.802632]  [] ? kthread_create_on_node+0x140/0x140

[795797.810207]  [] ret_from_fork+0x58/0x90

[795797.816646]  [] ? kthread_create_on_node+0x140/0x140


I find there are something different in upstream (in pick_next_entity )

static struct sched_entity *

pick_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *curr)

{
       struct sched_entity *left = __pick_first_entity(cfs_rq);
        struct sched_entity *se;
        /*
         * If curr is set we have to see if its left of the leftmost entity
         * still in the tree, provided there was anything in the tree at all.
         */
        if (!left || (curr && entity_before(curr, left)))
                left = curr;
        se = left; /* ideally we run the leftmost entity */
        /*
         * Avoid running the skip buddy, if running something else can
         * be done without getting too unfair.
         */
        if (cfs_rq->skip == se) {
                struct sched_entity *second;
                if (se == curr) {
                        second = __pick_first_entity(cfs_rq);
                } else {
                        second = __pick_next_entity(se);
                        if (!second || (curr && entity_before(curr, second)))
                                second = curr;
                }
                if (second && wakeup_preempt_entity(second, left) < 1)
                        se = second;
        }
        /*
         * Prefer last buddy, try to return the CPU to a preempted task.
         */
        if (cfs_rq->last && wakeup_preempt_entity(cfs_rq->last, left) < 1)
                se = cfs_rq->last;
        /*
         * Someone really wants this to run. If it's not unfair, run it.
         */
        if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1)
                se = cfs_rq->next;
        clear_buddies(cfs_rq, se);
        return se;

}

if the se,curr,cfs_rq->skip are all NULL, the wakeup_preempt_entity()
or  clear_buddies will be also occured the  "NULL pointer dereference"
problem.

I think the following patch can work in the case:

--- a/kernel/sched/fair.c

+++ b/kernel/sched/fair.c

@@ -4639,10 +4639,9 @@
    struct cfs_rq *cfs_rq = &rq->cfs;
    struct sched_entity *se;

-   if (!cfs_rq->nr_running)
-       return NULL;
-
    do {
+       if (!cfs_rq->nr_running)
+           return NULL;
        se = pick_next_entity(cfs_rq);
        set_next_entity(cfs_rq, se);
        cfs_rq = group_cfs_rq(se);



Do I miss some rules or this is a bug?

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ