summaryrefslogtreecommitdiffstats
path: root/net/tipc
diff options
context:
space:
mode:
authorJon Maloy <jon.maloy@ericsson.com>2018-05-08 20:59:41 -0400
committerDavid S. Miller <davem@davemloft.net>2018-05-10 15:25:45 -0400
commit5f30721c517a9c8512da3bc8d909ff5e810c2b44 (patch)
tree407a08b3bbd39d030ddd5830d99adccbdf596cb6 /net/tipc
parentf663706a33ce32df69a2b3e85a80cb5a690f2234 (diff)
tipc: clean up removal of binding table items
In commit be47e41d77fb ("tipc: fix use-after-free in tipc_nametbl_stop") we fixed a problem caused by premature release of service range items. That fix is correct, and solved the problem. However, it doesn't address the root of the problem, which is that we don't lookup the tipc_service -> service_range -> publication items in the correct hierarchical order. In this commit we try to make this right, and as a side effect obtain some code simplification. Acked-by: Ying Xue <ying.xue@windriver.com> Signed-off-by: Jon Maloy <jon.maloy@ericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc')
-rw-r--r--net/tipc/name_table.c103
1 files changed, 53 insertions, 50 deletions
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index dd1c4fa2eb78..bebe88cae07b 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -136,12 +136,12 @@ static struct tipc_service *tipc_service_create(u32 type, struct hlist_head *hd)
136} 136}
137 137
138/** 138/**
139 * tipc_service_find_range - find service range matching a service instance 139 * tipc_service_first_range - find first service range in tree matching instance
140 * 140 *
141 * Very time-critical, so binary search through range rb tree 141 * Very time-critical, so binary search through range rb tree
142 */ 142 */
143static struct service_range *tipc_service_find_range(struct tipc_service *sc, 143static struct service_range *tipc_service_first_range(struct tipc_service *sc,
144 u32 instance) 144 u32 instance)
145{ 145{
146 struct rb_node *n = sc->ranges.rb_node; 146 struct rb_node *n = sc->ranges.rb_node;
147 struct service_range *sr; 147 struct service_range *sr;
@@ -158,6 +158,30 @@ static struct service_range *tipc_service_find_range(struct tipc_service *sc,
158 return NULL; 158 return NULL;
159} 159}
160 160
161/* tipc_service_find_range - find service range matching publication parameters
162 */
163static struct service_range *tipc_service_find_range(struct tipc_service *sc,
164 u32 lower, u32 upper)
165{
166 struct rb_node *n = sc->ranges.rb_node;
167 struct service_range *sr;
168
169 sr = tipc_service_first_range(sc, lower);
170 if (!sr)
171 return NULL;
172
173 /* Look for exact match */
174 for (n = &sr->tree_node; n; n = rb_next(n)) {
175 sr = container_of(n, struct service_range, tree_node);
176 if (sr->upper == upper)
177 break;
178 }
179 if (!n || sr->lower != lower || sr->upper != upper)
180 return NULL;
181
182 return sr;
183}
184
161static struct service_range *tipc_service_create_range(struct tipc_service *sc, 185static struct service_range *tipc_service_create_range(struct tipc_service *sc,
162 u32 lower, u32 upper) 186 u32 lower, u32 upper)
163{ 187{
@@ -238,54 +262,19 @@ err:
238/** 262/**
239 * tipc_service_remove_publ - remove a publication from a service 263 * tipc_service_remove_publ - remove a publication from a service
240 */ 264 */
241static struct publication *tipc_service_remove_publ(struct net *net, 265static struct publication *tipc_service_remove_publ(struct service_range *sr,
242 struct tipc_service *sc, 266 u32 node, u32 key)
243 u32 lower, u32 upper,
244 u32 node, u32 key,
245 struct service_range **rng)
246{ 267{
247 struct tipc_subscription *sub, *tmp;
248 struct service_range *sr;
249 struct publication *p; 268 struct publication *p;
250 bool found = false;
251 bool last = false;
252 struct rb_node *n;
253
254 sr = tipc_service_find_range(sc, lower);
255 if (!sr)
256 return NULL;
257 269
258 /* Find exact matching service range */
259 for (n = &sr->tree_node; n; n = rb_next(n)) {
260 sr = container_of(n, struct service_range, tree_node);
261 if (sr->upper == upper)
262 break;
263 }
264 if (!n || sr->lower != lower || sr->upper != upper)
265 return NULL;
266
267 /* Find publication, if it exists */
268 list_for_each_entry(p, &sr->all_publ, all_publ) { 270 list_for_each_entry(p, &sr->all_publ, all_publ) {
269 if (p->key != key || (node && node != p->node)) 271 if (p->key != key || (node && node != p->node))
270 continue; 272 continue;
271 found = true; 273 list_del(&p->all_publ);
272 break; 274 list_del(&p->local_publ);
275 return p;
273 } 276 }
274 if (!found) 277 return NULL;
275 return NULL;
276
277 list_del(&p->all_publ);
278 list_del(&p->local_publ);
279 if (list_empty(&sr->all_publ))
280 last = true;
281
282 /* Notify any waiting subscriptions */
283 list_for_each_entry_safe(sub, tmp, &sc->subscriptions, service_list) {
284 tipc_sub_report_overlap(sub, p->lower, p->upper, TIPC_WITHDRAWN,
285 p->port, p->node, p->scope, last);
286 }
287 *rng = sr;
288 return p;
289} 278}
290 279
291/** 280/**
@@ -376,17 +365,31 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type,
376 u32 node, u32 key) 365 u32 node, u32 key)
377{ 366{
378 struct tipc_service *sc = tipc_service_find(net, type); 367 struct tipc_service *sc = tipc_service_find(net, type);
368 struct tipc_subscription *sub, *tmp;
379 struct service_range *sr = NULL; 369 struct service_range *sr = NULL;
380 struct publication *p = NULL; 370 struct publication *p = NULL;
371 bool last;
381 372
382 if (!sc) 373 if (!sc)
383 return NULL; 374 return NULL;
384 375
385 spin_lock_bh(&sc->lock); 376 spin_lock_bh(&sc->lock);
386 p = tipc_service_remove_publ(net, sc, lower, upper, node, key, &sr); 377 sr = tipc_service_find_range(sc, lower, upper);
378 if (!sr)
379 goto exit;
380 p = tipc_service_remove_publ(sr, node, key);
381 if (!p)
382 goto exit;
383
384 /* Notify any waiting subscriptions */
385 last = list_empty(&sr->all_publ);
386 list_for_each_entry_safe(sub, tmp, &sc->subscriptions, service_list) {
387 tipc_sub_report_overlap(sub, lower, upper, TIPC_WITHDRAWN,
388 p->port, node, p->scope, last);
389 }
387 390
388 /* Remove service range item if this was its last publication */ 391 /* Remove service range item if this was its last publication */
389 if (sr && list_empty(&sr->all_publ)) { 392 if (list_empty(&sr->all_publ)) {
390 rb_erase(&sr->tree_node, &sc->ranges); 393 rb_erase(&sr->tree_node, &sc->ranges);
391 kfree(sr); 394 kfree(sr);
392 } 395 }
@@ -396,6 +399,7 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type,
396 hlist_del_init_rcu(&sc->service_list); 399 hlist_del_init_rcu(&sc->service_list);
397 kfree_rcu(sc, rcu); 400 kfree_rcu(sc, rcu);
398 } 401 }
402exit:
399 spin_unlock_bh(&sc->lock); 403 spin_unlock_bh(&sc->lock);
400 return p; 404 return p;
401} 405}
@@ -437,7 +441,7 @@ u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *dnode)
437 goto not_found; 441 goto not_found;
438 442
439 spin_lock_bh(&sc->lock); 443 spin_lock_bh(&sc->lock);
440 sr = tipc_service_find_range(sc, instance); 444 sr = tipc_service_first_range(sc, instance);
441 if (unlikely(!sr)) 445 if (unlikely(!sr))
442 goto no_match; 446 goto no_match;
443 447
@@ -484,7 +488,7 @@ bool tipc_nametbl_lookup(struct net *net, u32 type, u32 instance, u32 scope,
484 488
485 spin_lock_bh(&sc->lock); 489 spin_lock_bh(&sc->lock);
486 490
487 sr = tipc_service_find_range(sc, instance); 491 sr = tipc_service_first_range(sc, instance);
488 if (!sr) 492 if (!sr)
489 goto no_match; 493 goto no_match;
490 494
@@ -756,8 +760,7 @@ static void tipc_service_delete(struct net *net, struct tipc_service *sc)
756 spin_lock_bh(&sc->lock); 760 spin_lock_bh(&sc->lock);
757 rbtree_postorder_for_each_entry_safe(sr, tmpr, &sc->ranges, tree_node) { 761 rbtree_postorder_for_each_entry_safe(sr, tmpr, &sc->ranges, tree_node) {
758 list_for_each_entry_safe(p, tmp, &sr->all_publ, all_publ) { 762 list_for_each_entry_safe(p, tmp, &sr->all_publ, all_publ) {
759 tipc_service_remove_publ(net, sc, p->lower, p->upper, 763 tipc_service_remove_publ(sr, p->node, p->key);
760 p->node, p->key, &sr);
761 kfree_rcu(p, rcu); 764 kfree_rcu(p, rcu);
762 } 765 }
763 rb_erase(&sr->tree_node, &sc->ranges); 766 rb_erase(&sr->tree_node, &sc->ranges);