diff options
author | Sowmini Varadhan <sowmini.varadhan@oracle.com> | 2016-07-14 06:51:01 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-07-15 14:36:57 -0400 |
commit | a93d01f5777e99f24b5b3948e06673ada148337c (patch) | |
tree | 1c8f2320199c9d7e0881fd6020c99d8fd967662d /net | |
parent | caeccd5180930eb8586771bb1935f4f2e456a8e8 (diff) |
RDS: TCP: avoid bad page reference in rds_tcp_listen_data_ready
As the existing comments in rds_tcp_listen_data_ready() indicate,
it is possible under some race-windows to get to this function with the
accept() socket. If that happens, we could run into a sequence whereby
thread 1 thread 2
rds_tcp_accept_one() thread
sets up new_sock via ->accept().
The sk_user_data is now
sock_def_readable
data comes in for new_sock,
->sk_data_ready is called, and
we land in rds_tcp_listen_data_ready
rds_tcp_set_callbacks()
takes the sk_callback_lock and
sets up sk_user_data to be the cp
read_lock sk_callback_lock
ready = cp
unlock sk_callback_lock
page fault on ready
In the above sequence, we end up with a panic on a bad page reference
when trying to execute (*ready)(). Instead we need to call
sock_def_readable() safely, which is what this patch achieves.
Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: Sowmini Varadhan <sowmini.varadhan@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/rds/tcp.c | 7 | ||||
-rw-r--r-- | net/rds/tcp.h | 1 | ||||
-rw-r--r-- | net/rds/tcp_listen.c | 2 |
3 files changed, 10 insertions, 0 deletions
diff --git a/net/rds/tcp.c b/net/rds/tcp.c index d24f6c142d03..b411bb764f07 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c | |||
@@ -551,6 +551,13 @@ static void rds_tcp_kill_sock(struct net *net) | |||
551 | } | 551 | } |
552 | } | 552 | } |
553 | 553 | ||
554 | void *rds_tcp_listen_sock_def_readable(struct net *net) | ||
555 | { | ||
556 | struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid); | ||
557 | |||
558 | return rtn->rds_tcp_listen_sock->sk->sk_user_data; | ||
559 | } | ||
560 | |||
554 | static int rds_tcp_dev_event(struct notifier_block *this, | 561 | static int rds_tcp_dev_event(struct notifier_block *this, |
555 | unsigned long event, void *ptr) | 562 | unsigned long event, void *ptr) |
556 | { | 563 | { |
diff --git a/net/rds/tcp.h b/net/rds/tcp.h index 1c3160faa963..9a1cc8906576 100644 --- a/net/rds/tcp.h +++ b/net/rds/tcp.h | |||
@@ -70,6 +70,7 @@ void rds_tcp_listen_stop(struct socket *); | |||
70 | void rds_tcp_listen_data_ready(struct sock *sk); | 70 | void rds_tcp_listen_data_ready(struct sock *sk); |
71 | int rds_tcp_accept_one(struct socket *sock); | 71 | int rds_tcp_accept_one(struct socket *sock); |
72 | int rds_tcp_keepalive(struct socket *sock); | 72 | int rds_tcp_keepalive(struct socket *sock); |
73 | void *rds_tcp_listen_sock_def_readable(struct net *net); | ||
73 | 74 | ||
74 | /* tcp_recv.c */ | 75 | /* tcp_recv.c */ |
75 | int rds_tcp_recv_init(void); | 76 | int rds_tcp_recv_init(void); |
diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index ca975a217a49..73040e319e4b 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c | |||
@@ -183,6 +183,8 @@ void rds_tcp_listen_data_ready(struct sock *sk) | |||
183 | */ | 183 | */ |
184 | if (sk->sk_state == TCP_LISTEN) | 184 | if (sk->sk_state == TCP_LISTEN) |
185 | rds_tcp_accept_work(sk); | 185 | rds_tcp_accept_work(sk); |
186 | else | ||
187 | ready = rds_tcp_listen_sock_def_readable(sock_net(sk)); | ||
186 | 188 | ||
187 | out: | 189 | out: |
188 | read_unlock_bh(&sk->sk_callback_lock); | 190 | read_unlock_bh(&sk->sk_callback_lock); |