Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rds: Incorrect reference counting in TCP socket creation

Commit 0933a578cd55 ("rds: tcp: use sock_create_lite() to create the
accept socket") has a reference counting issue in TCP socket creation
when accepting a new connection. The code uses sock_create_lite() to
create a kernel socket. But it does not do __module_get() on the
socket owner. When the connection is shutdown and sock_release() is
called to free the socket, the owner's reference count is decremented
and becomes incorrect. Note that this bug only shows up when the socket
owner is configured as a kernel module.

v2: Update comments

Fixes: 0933a578cd55 ("rds: tcp: use sock_create_lite() to create the accept socket")
Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Acked-by: Sowmini Varadhan <sowmini.varadhan@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ka-Cheong Poon and committed by
David S. Miller
84eef2b2 a5f7b0ee

+11 -3
+11 -3
net/rds/tcp_listen.c
··· 1 1 /* 2 - * Copyright (c) 2006 Oracle. All rights reserved. 2 + * Copyright (c) 2006, 2018 Oracle. All rights reserved. 3 3 * 4 4 * This software is available to you under a choice of one of two 5 5 * licenses. You may choose to be licensed under the terms of the GNU ··· 142 142 if (ret) 143 143 goto out; 144 144 145 - new_sock->type = sock->type; 146 - new_sock->ops = sock->ops; 147 145 ret = sock->ops->accept(sock, new_sock, O_NONBLOCK, true); 148 146 if (ret < 0) 149 147 goto out; 148 + 149 + /* sock_create_lite() does not get a hold on the owner module so we 150 + * need to do it here. Note that sock_release() uses sock->ops to 151 + * determine if it needs to decrement the reference count. So set 152 + * sock->ops after calling accept() in case that fails. And there's 153 + * no need to do try_module_get() as the listener should have a hold 154 + * already. 155 + */ 156 + new_sock->ops = sock->ops; 157 + __module_get(new_sock->ops->owner); 150 158 151 159 ret = rds_tcp_keepalive(new_sock); 152 160 if (ret < 0)