Skip to content

Commit 8a0f6eb

Browse files
ying-xuedavem330
authored andcommitted
tipc: involve reference counter for node structure
TIPC node hash node table is protected with rcu lock on read side. tipc_node_find() is used to look for a node object with node address through iterating the hash node table. As the entire process of what tipc_node_find() traverses the table is guarded with rcu read lock, it's safe for us. However, when callers use the node object returned by tipc_node_find(), there is no rcu read lock applied. Therefore, this is absolutely unsafe for callers of tipc_node_find(). Now we introduce a reference counter for node structure. Before tipc_node_find() returns node object to its caller, it first increases the reference counter. Accordingly, after its caller used it up, it decreases the counter again. This can prevent a node being used by one thread from being freed by another thread. Reviewed-by: Erik Hugne <[email protected]> Reviewed-by: Jon Maloy <[email protected]> Signed-off-by: Ying Xue <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent b952b2b commit 8a0f6eb

File tree

6 files changed

+79
-30
lines changed

6 files changed

+79
-30
lines changed

net/tipc/bcast.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,13 +329,12 @@ static void bclink_peek_nack(struct net *net, struct tipc_msg *msg)
329329
return;
330330

331331
tipc_node_lock(n_ptr);
332-
333332
if (n_ptr->bclink.recv_permitted &&
334333
(n_ptr->bclink.last_in != n_ptr->bclink.last_sent) &&
335334
(n_ptr->bclink.last_in == msg_bcgap_after(msg)))
336335
n_ptr->bclink.oos_state = 2;
337-
338336
tipc_node_unlock(n_ptr);
337+
tipc_node_put(n_ptr);
339338
}
340339

341340
/* tipc_bclink_xmit - deliver buffer chain to all nodes in cluster
@@ -466,6 +465,7 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf)
466465
tipc_node_unlock(node);
467466
bclink_peek_nack(net, msg);
468467
}
468+
tipc_node_put(node);
469469
goto exit;
470470
}
471471

@@ -570,6 +570,7 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf)
570570

571571
unlock:
572572
tipc_node_unlock(node);
573+
tipc_node_put(node);
573574
exit:
574575
kfree_skb(buf);
575576
}

net/tipc/discover.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *buf,
260260
}
261261
}
262262
tipc_node_unlock(node);
263+
tipc_node_put(node);
263264
}
264265

265266
/**

net/tipc/link.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dnode,
854854
if (link)
855855
rc = __tipc_link_xmit(net, link, list);
856856
tipc_node_unlock(node);
857+
tipc_node_put(node);
857858
}
858859
if (link)
859860
return rc;
@@ -1116,8 +1117,8 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr)
11161117
n_ptr = tipc_node_find(net, msg_prevnode(msg));
11171118
if (unlikely(!n_ptr))
11181119
goto discard;
1119-
tipc_node_lock(n_ptr);
11201120

1121+
tipc_node_lock(n_ptr);
11211122
/* Locate unicast link endpoint that should handle message */
11221123
l_ptr = n_ptr->links[b_ptr->identity];
11231124
if (unlikely(!l_ptr))
@@ -1205,6 +1206,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr)
12051206
skb = NULL;
12061207
unlock:
12071208
tipc_node_unlock(n_ptr);
1209+
tipc_node_put(n_ptr);
12081210
discard:
12091211
if (unlikely(skb))
12101212
kfree_skb(skb);
@@ -2236,7 +2238,6 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)
22362238
msg.seq = cb->nlh->nlmsg_seq;
22372239

22382240
rcu_read_lock();
2239-
22402241
if (prev_node) {
22412242
node = tipc_node_find(net, prev_node);
22422243
if (!node) {
@@ -2249,13 +2250,15 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)
22492250
cb->prev_seq = 1;
22502251
goto out;
22512252
}
2253+
tipc_node_put(node);
22522254

22532255
list_for_each_entry_continue_rcu(node, &tn->node_list,
22542256
list) {
22552257
tipc_node_lock(node);
22562258
err = __tipc_nl_add_node_links(net, &msg, node,
22572259
&prev_link);
22582260
tipc_node_unlock(node);
2261+
tipc_node_put(node);
22592262
if (err)
22602263
goto out;
22612264

net/tipc/name_distr.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ static void tipc_publ_subscribe(struct net *net, struct publication *publ,
244244
tipc_node_lock(node);
245245
list_add_tail(&publ->nodesub_list, &node->publ_list);
246246
tipc_node_unlock(node);
247+
tipc_node_put(node);
247248
}
248249

249250
static void tipc_publ_unsubscribe(struct net *net, struct publication *publ,
@@ -258,6 +259,7 @@ static void tipc_publ_unsubscribe(struct net *net, struct publication *publ,
258259
tipc_node_lock(node);
259260
list_del_init(&publ->nodesub_list);
260261
tipc_node_unlock(node);
262+
tipc_node_put(node);
261263
}
262264

263265
/**

net/tipc/node.c

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
static void node_lost_contact(struct tipc_node *n_ptr);
4444
static void node_established_contact(struct tipc_node *n_ptr);
45+
static void tipc_node_delete(struct tipc_node *node);
4546

4647
struct tipc_sock_conn {
4748
u32 port;
@@ -67,6 +68,23 @@ static unsigned int tipc_hashfn(u32 addr)
6768
return addr & (NODE_HTABLE_SIZE - 1);
6869
}
6970

71+
static void tipc_node_kref_release(struct kref *kref)
72+
{
73+
struct tipc_node *node = container_of(kref, struct tipc_node, kref);
74+
75+
tipc_node_delete(node);
76+
}
77+
78+
void tipc_node_put(struct tipc_node *node)
79+
{
80+
kref_put(&node->kref, tipc_node_kref_release);
81+
}
82+
83+
static void tipc_node_get(struct tipc_node *node)
84+
{
85+
kref_get(&node->kref);
86+
}
87+
7088
/*
7189
* tipc_node_find - locate specified node object, if it exists
7290
*/
@@ -82,6 +100,7 @@ struct tipc_node *tipc_node_find(struct net *net, u32 addr)
82100
hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)],
83101
hash) {
84102
if (node->addr == addr) {
103+
tipc_node_get(node);
85104
rcu_read_unlock();
86105
return node;
87106
}
@@ -106,6 +125,7 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
106125
}
107126
n_ptr->addr = addr;
108127
n_ptr->net = net;
128+
kref_init(&n_ptr->kref);
109129
spin_lock_init(&n_ptr->lock);
110130
INIT_HLIST_NODE(&n_ptr->hash);
111131
INIT_LIST_HEAD(&n_ptr->list);
@@ -120,16 +140,17 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
120140
list_add_tail_rcu(&n_ptr->list, &temp_node->list);
121141
n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
122142
n_ptr->signature = INVALID_NODE_SIG;
143+
tipc_node_get(n_ptr);
123144
exit:
124145
spin_unlock_bh(&tn->node_list_lock);
125146
return n_ptr;
126147
}
127148

128-
static void tipc_node_delete(struct tipc_net *tn, struct tipc_node *n_ptr)
149+
static void tipc_node_delete(struct tipc_node *node)
129150
{
130-
list_del_rcu(&n_ptr->list);
131-
hlist_del_rcu(&n_ptr->hash);
132-
kfree_rcu(n_ptr, rcu);
151+
list_del_rcu(&node->list);
152+
hlist_del_rcu(&node->hash);
153+
kfree_rcu(node, rcu);
133154
}
134155

135156
void tipc_node_stop(struct net *net)
@@ -139,14 +160,15 @@ void tipc_node_stop(struct net *net)
139160

140161
spin_lock_bh(&tn->node_list_lock);
141162
list_for_each_entry_safe(node, t_node, &tn->node_list, list)
142-
tipc_node_delete(tn, node);
163+
tipc_node_put(node);
143164
spin_unlock_bh(&tn->node_list_lock);
144165
}
145166

146167
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
147168
{
148169
struct tipc_node *node;
149170
struct tipc_sock_conn *conn;
171+
int err = 0;
150172

151173
if (in_own_node(net, dnode))
152174
return 0;
@@ -157,16 +179,20 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
157179
return -EHOSTUNREACH;
158180
}
159181
conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
160-
if (!conn)
161-
return -EHOSTUNREACH;
182+
if (!conn) {
183+
err = -EHOSTUNREACH;
184+
goto exit;
185+
}
162186
conn->peer_node = dnode;
163187
conn->port = port;
164188
conn->peer_port = peer_port;
165189

166190
tipc_node_lock(node);
167191
list_add_tail(&conn->list, &node->conn_sks);
168192
tipc_node_unlock(node);
169-
return 0;
193+
exit:
194+
tipc_node_put(node);
195+
return err;
170196
}
171197

172198
void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
@@ -189,6 +215,7 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
189215
kfree(conn);
190216
}
191217
tipc_node_unlock(node);
218+
tipc_node_put(node);
192219
}
193220

194221
/**
@@ -417,19 +444,25 @@ int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
417444
char *linkname, size_t len)
418445
{
419446
struct tipc_link *link;
447+
int err = -EINVAL;
420448
struct tipc_node *node = tipc_node_find(net, addr);
421449

422-
if ((bearer_id >= MAX_BEARERS) || !node)
423-
return -EINVAL;
450+
if (!node)
451+
return err;
452+
453+
if (bearer_id >= MAX_BEARERS)
454+
goto exit;
455+
424456
tipc_node_lock(node);
425457
link = node->links[bearer_id];
426458
if (link) {
427459
strncpy(linkname, link->name, len);
428-
tipc_node_unlock(node);
429-
return 0;
460+
err = 0;
430461
}
462+
exit:
431463
tipc_node_unlock(node);
432-
return -EINVAL;
464+
tipc_node_put(node);
465+
return err;
433466
}
434467

435468
void tipc_node_unlock(struct tipc_node *node)
@@ -545,17 +578,21 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
545578
msg.seq = cb->nlh->nlmsg_seq;
546579

547580
rcu_read_lock();
548-
549-
if (last_addr && !tipc_node_find(net, last_addr)) {
550-
rcu_read_unlock();
551-
/* We never set seq or call nl_dump_check_consistent() this
552-
* means that setting prev_seq here will cause the consistence
553-
* check to fail in the netlink callback handler. Resulting in
554-
* the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if
555-
* the node state changed while we released the lock.
556-
*/
557-
cb->prev_seq = 1;
558-
return -EPIPE;
581+
if (last_addr) {
582+
node = tipc_node_find(net, last_addr);
583+
if (!node) {
584+
rcu_read_unlock();
585+
/* We never set seq or call nl_dump_check_consistent()
586+
* this means that setting prev_seq here will cause the
587+
* consistence check to fail in the netlink callback
588+
* handler. Resulting in the NLMSG_DONE message having
589+
* the NLM_F_DUMP_INTR flag set if the node state
590+
* changed while we released the lock.
591+
*/
592+
cb->prev_seq = 1;
593+
return -EPIPE;
594+
}
595+
tipc_node_put(node);
559596
}
560597

561598
list_for_each_entry_rcu(node, &tn->node_list, list) {

net/tipc/node.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ struct tipc_node_bclink {
9494
/**
9595
* struct tipc_node - TIPC node structure
9696
* @addr: network address of node
97+
* @ref: reference counter to node object
9798
* @lock: spinlock governing access to structure
9899
* @net: the applicable net namespace
99100
* @hash: links to adjacent nodes in unsorted hash chain
@@ -115,6 +116,7 @@ struct tipc_node_bclink {
115116
*/
116117
struct tipc_node {
117118
u32 addr;
119+
struct kref kref;
118120
spinlock_t lock;
119121
struct net *net;
120122
struct hlist_node hash;
@@ -137,6 +139,7 @@ struct tipc_node {
137139
};
138140

139141
struct tipc_node *tipc_node_find(struct net *net, u32 addr);
142+
void tipc_node_put(struct tipc_node *node);
140143
struct tipc_node *tipc_node_create(struct net *net, u32 addr);
141144
void tipc_node_stop(struct net *net);
142145
void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr);
@@ -171,10 +174,12 @@ static inline uint tipc_node_get_mtu(struct net *net, u32 addr, u32 selector)
171174

172175
node = tipc_node_find(net, addr);
173176

174-
if (likely(node))
177+
if (likely(node)) {
175178
mtu = node->act_mtus[selector & 1];
176-
else
179+
tipc_node_put(node);
180+
} else {
177181
mtu = MAX_MSG_SIZE;
182+
}
178183

179184
return mtu;
180185
}

0 commit comments

Comments
 (0)