centos6.6 pidns_get空指针问题

某天数据库的同学抱怨服务器不断重启,登录发现均有crash的记录,查看之后发现是最近某支撑部门部署的agent导致,而且集中在centos6.6的系统中,crash中dmesg中有如下信息:

PID: 4361   TASK: ffff881024318aa0  CPU: 23  COMMAND: "java"
……
    [exception RIP: kref_get+12]
    RIP: ffffffff8128f42c  RSP: ffff881022f61e38  RFLAGS: 00010292
    RAX: 0000000000000000  RBX: 0000000000000000  RCX: 00000000fffffff3
    RDX: 0000000000000000  RSI: 0000000000000000  RDI: 0000000000000000
    RBP: ffff881022f61e48   R8: 0000000000000000   R9: 0000000000000000
    R10: 0000000000000000  R11: 0000000000000001  R12: ffffffff8161b040
    R13: 0000000000001001  R14: 00007f1bf4dfd2a0  R15: 0000000000000000
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
#9 [ffff881022f61e50] pidns_get at ffffffff810d69f6
#10 [ffff881022f61e70] proc_ns_readlink at ffffffff81203558

这里截取了关键的部分,发现是在pidns_get中调用了kref_get,但是kref_get获取到的参数,是空的指针,所以导致出错,我们查看了pidns_get的定义:

static void *pidns_get(struct task_struct *task)
{
        struct pid_namespace *ns;

        rcu_read_lock();
        ns = get_pid_ns(task_active_pid_ns(task));
        rcu_read_unlock();

        return ns;
}

这里因为task_active_pid_ns(task)可能会返回NULL,但是get_pid_ns是这么定义的:

static inline struct pid_namespace *get_pid_ns(struct pid_namespace *ns)
{
        if (ns != &init_pid_ns)
                kref_get(&ns->kref);
        return ns;
}

这里kref_get 去拿&ns->kref的时候,因为ns是NULL,所以会发生空指针访问异常。
正常是需要判断值为NULL的逻辑的,但是这里并没有,我们对比观察下没有问题的centos6.8系统上的代码:

static void *pidns_get(struct task_struct *task)
{
        struct pid_namespace *ns;

        rcu_read_lock();
        ns = task_active_pid_ns(task);
        if (ns)
                get_pid_ns(ns);
        rcu_read_unlock();

        return ns;
}

发现这里判断了当ns不为NULL的时候,才会去执行get_pid_ns(),这样自然不会有问题。

此条目发表在好玩的linux分类目录。将固定链接加入收藏夹。