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

RDS: have sockets get transport module references

Right now there's nothing to stop the various paths that use
rs->rs_transport from racing with rmmod and executing freed transport
code. The simple fix is to have binding to a transport also hold a
reference to the transport's module, removing this class of races.

We already had an unused t_owner field which was set for the modular
transports and which wasn't set for the built-in loop transport.

Signed-off-by: Zach Brown <zach.brown@oracle.com>

authored by

Zach Brown and committed by
Andy Grover
5adb5bc6 77510481

+21 -6
+2
net/rds/af_rds.c
··· 90 90 rds_sock_count--; 91 91 spin_unlock_irqrestore(&rds_sock_lock, flags); 92 92 93 + rds_trans_put(rs->rs_transport); 94 + 93 95 sock->sk = NULL; 94 96 sock_put(sk); 95 97 out:
+4 -1
net/rds/connection.c
··· 117 117 { 118 118 struct rds_connection *conn, *parent = NULL; 119 119 struct hlist_head *head = rds_conn_bucket(laddr, faddr); 120 + struct rds_transport *loop_trans; 120 121 unsigned long flags; 121 122 int ret; 122 123 ··· 164 163 * can bind to the destination address then we'd rather the messages 165 164 * flow through loopback rather than either transport. 166 165 */ 167 - if (rds_trans_get_preferred(faddr)) { 166 + loop_trans = rds_trans_get_preferred(faddr); 167 + if (loop_trans) { 168 + rds_trans_put(loop_trans); 168 169 conn->c_loopback = 1; 169 170 if (is_outgoing && trans->t_prefer_loopback) { 170 171 /* "outgoing" connection - and the transport
+1
net/rds/rds.h
··· 798 798 int rds_trans_register(struct rds_transport *trans); 799 799 void rds_trans_unregister(struct rds_transport *trans); 800 800 struct rds_transport *rds_trans_get_preferred(__be32 addr); 801 + void rds_trans_put(struct rds_transport *trans); 801 802 unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter, 802 803 unsigned int avail); 803 804 int rds_trans_init(void);
+14 -5
net/rds/transport.c
··· 71 71 } 72 72 EXPORT_SYMBOL_GPL(rds_trans_unregister); 73 73 74 + void rds_trans_put(struct rds_transport *trans) 75 + { 76 + if (trans && trans->t_owner) 77 + module_put(trans->t_owner); 78 + } 79 + 74 80 struct rds_transport *rds_trans_get_preferred(__be32 addr) 75 81 { 76 82 struct rds_transport *ret = NULL; 77 - int i; 83 + struct rds_transport *trans; 84 + unsigned int i; 78 85 79 86 if (IN_LOOPBACK(ntohl(addr))) 80 87 return &rds_loop_transport; 81 88 82 89 down_read(&rds_trans_sem); 83 - for (i = 0; i < RDS_TRANS_COUNT; i++) 84 - { 85 - if (transports[i] && (transports[i]->laddr_check(addr) == 0)) { 86 - ret = transports[i]; 90 + for (i = 0; i < RDS_TRANS_COUNT; i++) { 91 + trans = transports[i]; 92 + 93 + if (trans && (trans->laddr_check(addr) == 0) && 94 + (!trans->t_owner || try_module_get(trans->t_owner))) { 95 + ret = trans; 87 96 break; 88 97 } 89 98 }