Skip to content

Commit 5f30721

Browse files
Jon Maloydavem330
authored andcommitted
tipc: clean up removal of binding table items
In commit be47e41 ("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 <[email protected]> Signed-off-by: Jon Maloy <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f663706 commit 5f30721

File tree

1 file changed

+53
-50
lines changed

1 file changed

+53
-50
lines changed

net/tipc/name_table.c

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,12 @@ static struct tipc_service *tipc_service_create(u32 type, struct hlist_head *hd)
136136
}
137137

138138
/**
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
140140
*
141141
* Very time-critical, so binary search through range rb tree
142142
*/
143-
static struct service_range *tipc_service_find_range(struct tipc_service *sc,
144-
u32 instance)
143+
static struct service_range *tipc_service_first_range(struct tipc_service *sc,
144+
u32 instance)
145145
{
146146
struct rb_node *n = sc->ranges.rb_node;
147147
struct service_range *sr;
@@ -158,6 +158,30 @@ static struct service_range *tipc_service_find_range(struct tipc_service *sc,
158158
return NULL;
159159
}
160160

161+
/* tipc_service_find_range - find service range matching publication parameters
162+
*/
163+
static 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+
161185
static struct service_range *tipc_service_create_range(struct tipc_service *sc,
162186
u32 lower, u32 upper)
163187
{
@@ -238,54 +262,19 @@ static struct publication *tipc_service_insert_publ(struct net *net,
238262
/**
239263
* tipc_service_remove_publ - remove a publication from a service
240264
*/
241-
static struct publication *tipc_service_remove_publ(struct net *net,
242-
struct tipc_service *sc,
243-
u32 lower, u32 upper,
244-
u32 node, u32 key,
245-
struct service_range **rng)
265+
static struct publication *tipc_service_remove_publ(struct service_range *sr,
266+
u32 node, u32 key)
246267
{
247-
struct tipc_subscription *sub, *tmp;
248-
struct service_range *sr;
249268
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;
257269

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 */
268270
list_for_each_entry(p, &sr->all_publ, all_publ) {
269271
if (p->key != key || (node && node != p->node))
270272
continue;
271-
found = true;
272-
break;
273+
list_del(&p->all_publ);
274+
list_del(&p->local_publ);
275+
return p;
273276
}
274-
if (!found)
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;
277+
return NULL;
289278
}
290279

291280
/**
@@ -376,17 +365,31 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type,
376365
u32 node, u32 key)
377366
{
378367
struct tipc_service *sc = tipc_service_find(net, type);
368+
struct tipc_subscription *sub, *tmp;
379369
struct service_range *sr = NULL;
380370
struct publication *p = NULL;
371+
bool last;
381372

382373
if (!sc)
383374
return NULL;
384375

385376
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+
}
387390

388391
/* 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)) {
390393
rb_erase(&sr->tree_node, &sc->ranges);
391394
kfree(sr);
392395
}
@@ -396,6 +399,7 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type,
396399
hlist_del_init_rcu(&sc->service_list);
397400
kfree_rcu(sc, rcu);
398401
}
402+
exit:
399403
spin_unlock_bh(&sc->lock);
400404
return p;
401405
}
@@ -437,7 +441,7 @@ u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *dnode)
437441
goto not_found;
438442

439443
spin_lock_bh(&sc->lock);
440-
sr = tipc_service_find_range(sc, instance);
444+
sr = tipc_service_first_range(sc, instance);
441445
if (unlikely(!sr))
442446
goto no_match;
443447

@@ -484,7 +488,7 @@ bool tipc_nametbl_lookup(struct net *net, u32 type, u32 instance, u32 scope,
484488

485489
spin_lock_bh(&sc->lock);
486490

487-
sr = tipc_service_find_range(sc, instance);
491+
sr = tipc_service_first_range(sc, instance);
488492
if (!sr)
489493
goto no_match;
490494

@@ -756,8 +760,7 @@ static void tipc_service_delete(struct net *net, struct tipc_service *sc)
756760
spin_lock_bh(&sc->lock);
757761
rbtree_postorder_for_each_entry_safe(sr, tmpr, &sc->ranges, tree_node) {
758762
list_for_each_entry_safe(p, tmp, &sr->all_publ, all_publ) {
759-
tipc_service_remove_publ(net, sc, p->lower, p->upper,
760-
p->node, p->key, &sr);
763+
tipc_service_remove_publ(sr, p->node, p->key);
761764
kfree_rcu(p, rcu);
762765
}
763766
rb_erase(&sr->tree_node, &sc->ranges);

0 commit comments

Comments
 (0)