Orabug:
20070989
BUG: unable to handle kernel NULL pointer dereference at
0000000000000008
IP: [<
ffffffffa02b639f>] sdp_rx_irq+0x4f/0x160 [ib_sdp]
PGD
1d7fd14067 PUD
190984b067 PMD 0
Oops: 0000 [#1] SMP
...
Pid: 61889, comm: oracle Not tainted 2.6.39-400.128.20.el5uek #1 Oracle
Corporation SUN FIRE X4170 M3 /ASSY,MOTHERBOARD,1U
RIP: 0010:[<
ffffffffa02b639f>] [<
ffffffffa02b639f>] sdp_rx_irq+0x4f/0x160
[ib_sdp]
...
Crash occurs in the call to sdp_sk_sleep(sk) through waitqueue_active():
drivers/infiniband/ulp/sdp/sdp_rx.c
static void sdp_rx_irq(struct ib_cq *cq, void *cq_context)
if (should_wake_up(sk)) {
drivers/infiniband/ulp/sdp/sdp_rx.c
static inline int should_wake_up(struct sock *sk)
{
return sdp_sk_sleep(sk) && waitqueue_active(sdp_sk_sleep(sk)) &&
(posts_handler(sdp_sk(sk)) || somebody_is_waiting(sk));
}
drivers/infiniband/ulp/sdp/sdp.h:
#define sdp_sk_sleep(sk) sk_sleep(sk)
include/net/sock.h
static inline wait_queue_head_t *sk_sleep(struct sock *sk)
{
BUILD_BUG_ON(offsetof(struct socket_wq, wait) != 0);
return &rcu_dereference_raw(sk->sk_wq)->wait;
}
We know the first call to sdp_sk_sleep(sk) finds a non-null sk->sk_wq
because we don't crash:
0xffffffffa02b6388 <sdp_rx_irq+56>: mov 0xb8(%rsi),%rax
0xffffffffa02b638f <sdp_rx_irq+63>: test %rax,%rax
*** struct sock sk+0xb8 == sk->sk_wq (sk_wq is at offset 0xb8)
*** we didn't crash at sdp_rx_irq+56 so sk->sk_wq was apparently valid
0xffffffffa02b6394 <sdp_rx_irq+68>: mov 0xb8(%rsi),%rdx
0xffffffffa02b639b <sdp_rx_irq+75>: lea 0x8(%rdx),%rax
0xffffffffa02b639f <sdp_rx_irq+79>: cmp %rax,0x8(%rdx)
*** RDX is NULL causing the null dereference of address 0x8 at sdp_rx_irq+79.
Fix is to check if sk->sk_wq is NULL before dereferencing it to get the
address of sk->sk_wq->wait. Also, do the RCU dereference of sk->sk_wq
once, not twice as we may get a different answer (NULL) the second time.
Signed-off-by: Chuck Anderson <chuck.anderson@oracle.com>
Signed-off-by: John Sobecki <john.sobecki@oracle.com>
Acked-by: Chien Yen <chien.yen@oracle.com>
Signed-off-by: Guangyu Sun <guangyu.sun@oracle.com>
}
-static inline int should_wake_up(struct sock *sk)
+/*
+ * Should be called with rcu_read_lock() held for the reference to wq.
+ * Returns:
+ * 1: there is probably a waiter
+ * 0: no waiter
+ */
+static inline int should_wake_up(struct sock *sk, struct socket_wq *wq)
{
- return sdp_sk_sleep(sk) && waitqueue_active(sdp_sk_sleep(sk)) &&
- (posts_handler(sdp_sk(sk)) || somebody_is_waiting(sk));
+ int rc;
+
+ if (unlikely(wq))
+ rc = waitqueue_active(&wq->wait) &&
+ (posts_handler(sdp_sk(sk)) || somebody_is_waiting(sk));
+ else
+ rc = 0;
+
+ return rc;
}
static void sdp_rx_irq(struct ib_cq *cq, void *cq_context)
{
struct sock *sk = cq_context;
struct sdp_sock *ssk = sdp_sk(sk);
+ struct socket_wq *wq;
if (unlikely(cq != ssk->rx_ring.cq)) {
sdp_warn(sk, "cq = %p, ssk->cq = %p\n", cq, ssk->rx_ring.cq);
sdp_prf(sk, NULL, "rx irq");
- if (should_wake_up(sk)) {
- wake_up_interruptible(sdp_sk_sleep(sk));
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (should_wake_up(sk, wq)) {
+ wake_up_interruptible(&wq->wait);
SDPSTATS_COUNTER_INC(rx_int_wake_up);
} else {
if (queue_work_on(ssk->cpu, rx_comp_wq, &ssk->rx_comp_work))
else
SDPSTATS_COUNTER_INC(rx_int_no_op);
}
+ rcu_read_unlock();
}
static void sdp_rx_ring_purge(struct sdp_sock *ssk)