Skip to content

Commit dade3f6

Browse files
dsahernPaolo Abeni
authored andcommitted
net/ipv6: Revert remove expired routes with a separated list of routes
This reverts commit 3dec89b. The commit has some race conditions given how expires is managed on a fib6_info in relation to gc start, adding the entry to the gc list and setting the timer value leading to UAF. Revert the commit and try again in a later release. Fixes: 3dec89b ("net/ipv6: Remove expired routes with a separated list of routes") Cc: Kui-Feng Lee <[email protected]> Signed-off-by: David Ahern <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent b414020 commit dade3f6

File tree

3 files changed

+22
-103
lines changed

3 files changed

+22
-103
lines changed

include/net/ip6_fib.h

Lines changed: 13 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,6 @@ struct fib6_info {
179179

180180
refcount_t fib6_ref;
181181
unsigned long expires;
182-
183-
struct hlist_node gc_link;
184-
185182
struct dst_metrics *fib6_metrics;
186183
#define fib6_pmtu fib6_metrics->metrics[RTAX_MTU-1]
187184

@@ -250,18 +247,26 @@ static inline bool fib6_requires_src(const struct fib6_info *rt)
250247
return rt->fib6_src.plen > 0;
251248
}
252249

250+
static inline void fib6_clean_expires(struct fib6_info *f6i)
251+
{
252+
f6i->fib6_flags &= ~RTF_EXPIRES;
253+
f6i->expires = 0;
254+
}
255+
256+
static inline void fib6_set_expires(struct fib6_info *f6i,
257+
unsigned long expires)
258+
{
259+
f6i->expires = expires;
260+
f6i->fib6_flags |= RTF_EXPIRES;
261+
}
262+
253263
static inline bool fib6_check_expired(const struct fib6_info *f6i)
254264
{
255265
if (f6i->fib6_flags & RTF_EXPIRES)
256266
return time_after(jiffies, f6i->expires);
257267
return false;
258268
}
259269

260-
static inline bool fib6_has_expires(const struct fib6_info *f6i)
261-
{
262-
return f6i->fib6_flags & RTF_EXPIRES;
263-
}
264-
265270
/* Function to safely get fn->fn_sernum for passed in rt
266271
* and store result in passed in cookie.
267272
* Return true if we can get cookie safely
@@ -383,7 +388,6 @@ struct fib6_table {
383388
struct inet_peer_base tb6_peers;
384389
unsigned int flags;
385390
unsigned int fib_seq;
386-
struct hlist_head tb6_gc_hlist; /* GC candidates */
387391
#define RT6_TABLE_HAS_DFLT_ROUTER BIT(0)
388392
};
389393

@@ -500,48 +504,6 @@ void fib6_gc_cleanup(void);
500504

501505
int fib6_init(void);
502506

503-
/* fib6_info must be locked by the caller, and fib6_info->fib6_table can be
504-
* NULL.
505-
*/
506-
static inline void fib6_set_expires_locked(struct fib6_info *f6i,
507-
unsigned long expires)
508-
{
509-
struct fib6_table *tb6;
510-
511-
tb6 = f6i->fib6_table;
512-
f6i->expires = expires;
513-
if (tb6 && !fib6_has_expires(f6i))
514-
hlist_add_head(&f6i->gc_link, &tb6->tb6_gc_hlist);
515-
f6i->fib6_flags |= RTF_EXPIRES;
516-
}
517-
518-
/* fib6_info must be locked by the caller, and fib6_info->fib6_table can be
519-
* NULL. If fib6_table is NULL, the fib6_info will no be inserted into the
520-
* list of GC candidates until it is inserted into a table.
521-
*/
522-
static inline void fib6_set_expires(struct fib6_info *f6i,
523-
unsigned long expires)
524-
{
525-
spin_lock_bh(&f6i->fib6_table->tb6_lock);
526-
fib6_set_expires_locked(f6i, expires);
527-
spin_unlock_bh(&f6i->fib6_table->tb6_lock);
528-
}
529-
530-
static inline void fib6_clean_expires_locked(struct fib6_info *f6i)
531-
{
532-
if (fib6_has_expires(f6i))
533-
hlist_del_init(&f6i->gc_link);
534-
f6i->fib6_flags &= ~RTF_EXPIRES;
535-
f6i->expires = 0;
536-
}
537-
538-
static inline void fib6_clean_expires(struct fib6_info *f6i)
539-
{
540-
spin_lock_bh(&f6i->fib6_table->tb6_lock);
541-
fib6_clean_expires_locked(f6i);
542-
spin_unlock_bh(&f6i->fib6_table->tb6_lock);
543-
}
544-
545507
struct ipv6_route_iter {
546508
struct seq_net_private p;
547509
struct fib6_walker w;

net/ipv6/ip6_fib.c

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,6 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh)
160160
INIT_LIST_HEAD(&f6i->fib6_siblings);
161161
refcount_set(&f6i->fib6_ref, 1);
162162

163-
INIT_HLIST_NODE(&f6i->gc_link);
164-
165163
return f6i;
166164
}
167165

@@ -248,7 +246,6 @@ static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
248246
net->ipv6.fib6_null_entry);
249247
table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
250248
inet_peer_base_init(&table->tb6_peers);
251-
INIT_HLIST_HEAD(&table->tb6_gc_hlist);
252249
}
253250

254251
return table;
@@ -1060,8 +1057,6 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
10601057
lockdep_is_held(&table->tb6_lock));
10611058
}
10621059
}
1063-
1064-
fib6_clean_expires_locked(rt);
10651060
}
10661061

10671062
/*
@@ -1123,10 +1118,9 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
11231118
if (!(iter->fib6_flags & RTF_EXPIRES))
11241119
return -EEXIST;
11251120
if (!(rt->fib6_flags & RTF_EXPIRES))
1126-
fib6_clean_expires_locked(iter);
1121+
fib6_clean_expires(iter);
11271122
else
1128-
fib6_set_expires_locked(iter,
1129-
rt->expires);
1123+
fib6_set_expires(iter, rt->expires);
11301124

11311125
if (rt->fib6_pmtu)
11321126
fib6_metric_set(iter, RTAX_MTU,
@@ -1485,10 +1479,6 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
14851479
if (rt->nh)
14861480
list_add(&rt->nh_list, &rt->nh->f6i_list);
14871481
__fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net));
1488-
1489-
if (fib6_has_expires(rt))
1490-
hlist_add_head(&rt->gc_link, &table->tb6_gc_hlist);
1491-
14921482
fib6_start_gc(info->nl_net, rt);
14931483
}
14941484

@@ -2291,16 +2281,17 @@ static void fib6_flush_trees(struct net *net)
22912281
* Garbage collection
22922282
*/
22932283

2294-
static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
2284+
static int fib6_age(struct fib6_info *rt, void *arg)
22952285
{
2286+
struct fib6_gc_args *gc_args = arg;
22962287
unsigned long now = jiffies;
22972288

22982289
/*
22992290
* check addrconf expiration here.
23002291
* Routes are expired even if they are in use.
23012292
*/
23022293

2303-
if (fib6_has_expires(rt) && rt->expires) {
2294+
if (rt->fib6_flags & RTF_EXPIRES && rt->expires) {
23042295
if (time_after(now, rt->expires)) {
23052296
RT6_TRACE("expiring %p\n", rt);
23062297
return -1;
@@ -2317,40 +2308,6 @@ static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
23172308
return 0;
23182309
}
23192310

2320-
static void fib6_gc_table(struct net *net,
2321-
struct fib6_table *tb6,
2322-
struct fib6_gc_args *gc_args)
2323-
{
2324-
struct fib6_info *rt;
2325-
struct hlist_node *n;
2326-
struct nl_info info = {
2327-
.nl_net = net,
2328-
.skip_notify = false,
2329-
};
2330-
2331-
hlist_for_each_entry_safe(rt, n, &tb6->tb6_gc_hlist, gc_link)
2332-
if (fib6_age(rt, gc_args) == -1)
2333-
fib6_del(rt, &info);
2334-
}
2335-
2336-
static void fib6_gc_all(struct net *net, struct fib6_gc_args *gc_args)
2337-
{
2338-
struct fib6_table *table;
2339-
struct hlist_head *head;
2340-
unsigned int h;
2341-
2342-
rcu_read_lock();
2343-
for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
2344-
head = &net->ipv6.fib_table_hash[h];
2345-
hlist_for_each_entry_rcu(table, head, tb6_hlist) {
2346-
spin_lock_bh(&table->tb6_lock);
2347-
fib6_gc_table(net, table, gc_args);
2348-
spin_unlock_bh(&table->tb6_lock);
2349-
}
2350-
}
2351-
rcu_read_unlock();
2352-
}
2353-
23542311
void fib6_run_gc(unsigned long expires, struct net *net, bool force)
23552312
{
23562313
struct fib6_gc_args gc_args;
@@ -2366,7 +2323,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force)
23662323
net->ipv6.sysctl.ip6_rt_gc_interval;
23672324
gc_args.more = 0;
23682325

2369-
fib6_gc_all(net, &gc_args);
2326+
fib6_clean_all(net, fib6_age, &gc_args);
23702327
now = jiffies;
23712328
net->ipv6.ip6_rt_last_gc = now;
23722329

net/ipv6/route.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3763,10 +3763,10 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
37633763
rt->dst_nocount = true;
37643764

37653765
if (cfg->fc_flags & RTF_EXPIRES)
3766-
fib6_set_expires_locked(rt, jiffies +
3767-
clock_t_to_jiffies(cfg->fc_expires));
3766+
fib6_set_expires(rt, jiffies +
3767+
clock_t_to_jiffies(cfg->fc_expires));
37683768
else
3769-
fib6_clean_expires_locked(rt);
3769+
fib6_clean_expires(rt);
37703770

37713771
if (cfg->fc_protocol == RTPROT_UNSPEC)
37723772
cfg->fc_protocol = RTPROT_BOOT;

0 commit comments

Comments
 (0)