Skip to content

Commit

Permalink
Merge branch 'rds-tcp-netns-delete-related-fixes'
Browse files Browse the repository at this point in the history
Sowmini Varadhan says:

====================
rds-tcp netns delete related fixes

Patchset contains cleanup and bug fixes. Patch 1 is the removal
of some redundant code/functions. Patch 2 and 3 are fixes for
corner cases identified by syzkaller. I've not been able to
reproduce the actual use-after-free race flagged in the syzkaller
reports, thus these fixes are based on code inspection plus
manual testing to make sure the modified code paths are executed
without problems in the commonly encountered timing cases.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Dec 1, 2017
2 parents 4c94cc2 + f10b4cf commit 68bf33f
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 32 deletions.
3 changes: 2 additions & 1 deletion net/rds/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ void rds_conn_shutdown(struct rds_conn_path *cp)
* to the conn hash, so we never trigger a reconnect on this
* conn - the reconnect is always triggered by the active peer. */
cancel_delayed_work_sync(&cp->cp_conn_w);
if (conn->c_destroy_in_prog)
return;
rcu_read_lock();
if (!hlist_unhashed(&conn->c_hash_node)) {
rcu_read_unlock();
Expand Down Expand Up @@ -445,7 +447,6 @@ void rds_conn_destroy(struct rds_connection *conn)
*/
rds_cong_remove_conn(conn);

put_net(conn->c_net);
kfree(conn->c_path);
kmem_cache_free(rds_conn_slab, conn);

Expand Down
6 changes: 3 additions & 3 deletions net/rds/rds.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ struct rds_connection {

/* Protocol version */
unsigned int c_version;
struct net *c_net;
possible_net_t c_net;

struct list_head c_map_item;
unsigned long c_map_queued;
Expand All @@ -165,13 +165,13 @@ struct rds_connection {
static inline
struct net *rds_conn_net(struct rds_connection *conn)
{
return conn->c_net;
return read_pnet(&conn->c_net);
}

static inline
void rds_conn_net_set(struct rds_connection *conn, struct net *net)
{
conn->c_net = get_net(net);
write_pnet(&conn->c_net, net);
}

#define RDS_FLAG_CONG_BITMAP 0x01
Expand Down
38 changes: 10 additions & 28 deletions net/rds/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,8 @@ static void rds_tcp_conn_free(void *arg)
rdsdebug("freeing tc %p\n", tc);

spin_lock_irqsave(&rds_tcp_conn_lock, flags);
list_del(&tc->t_tcp_node);
if (!tc->t_tcp_node_detached)
list_del(&tc->t_tcp_node);
spin_unlock_irqrestore(&rds_tcp_conn_lock, flags);

kmem_cache_free(rds_tcp_conn_slab, tc);
Expand Down Expand Up @@ -495,27 +496,6 @@ static struct pernet_operations rds_tcp_net_ops = {
.size = sizeof(struct rds_tcp_net),
};

/* explicitly send a RST on each socket, thereby releasing any socket refcnts
* that may otherwise hold up netns deletion.
*/
static void rds_tcp_conn_paths_destroy(struct rds_connection *conn)
{
struct rds_conn_path *cp;
struct rds_tcp_connection *tc;
int i;
struct sock *sk;

for (i = 0; i < RDS_MPATH_WORKERS; i++) {
cp = &conn->c_path[i];
tc = cp->cp_transport_data;
if (!tc->t_sock)
continue;
sk = tc->t_sock->sk;
sk->sk_prot->disconnect(sk, 0);
tcp_done(sk);
}
}

static void rds_tcp_kill_sock(struct net *net)
{
struct rds_tcp_connection *tc, *_tc;
Expand All @@ -527,18 +507,20 @@ static void rds_tcp_kill_sock(struct net *net)
rds_tcp_listen_stop(lsock, &rtn->rds_tcp_accept_w);
spin_lock_irq(&rds_tcp_conn_lock);
list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
struct net *c_net = tc->t_cpath->cp_conn->c_net;
struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net);

if (net != c_net || !tc->t_sock)
continue;
if (!list_has_conn(&tmp_list, tc->t_cpath->cp_conn))
if (!list_has_conn(&tmp_list, tc->t_cpath->cp_conn)) {
list_move_tail(&tc->t_tcp_node, &tmp_list);
} else {
list_del(&tc->t_tcp_node);
tc->t_tcp_node_detached = true;
}
}
spin_unlock_irq(&rds_tcp_conn_lock);
list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node) {
rds_tcp_conn_paths_destroy(tc->t_cpath->cp_conn);
list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node)
rds_conn_destroy(tc->t_cpath->cp_conn);
}
}

void *rds_tcp_listen_sock_def_readable(struct net *net)
Expand Down Expand Up @@ -586,7 +568,7 @@ static void rds_tcp_sysctl_reset(struct net *net)

spin_lock_irq(&rds_tcp_conn_lock);
list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
struct net *c_net = tc->t_cpath->cp_conn->c_net;
struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net);

if (net != c_net || !tc->t_sock)
continue;
Expand Down
1 change: 1 addition & 0 deletions net/rds/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct rds_tcp_incoming {
struct rds_tcp_connection {

struct list_head t_tcp_node;
bool t_tcp_node_detached;
struct rds_conn_path *t_cpath;
/* t_conn_path_lock synchronizes the connection establishment between
* rds_tcp_accept_one and rds_tcp_conn_path_connect
Expand Down

0 comments on commit 68bf33f

Please sign in to comment.