@@ -216,6 +216,9 @@ struct rt6_info {
/* more non-fragment space at head required */
unsigned short rt6i_nfheader_len;
+
+ /* route lookup always acquires a reference */
+ bool rt6i_count_held;
};
struct fib6_result {
@@ -2251,6 +2251,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
* this refcnt is always returned to the caller even
* if caller sets RT6_LOOKUP_F_DST_NOREF flag.
*/
+ rt->rt6i_count_held = true;
rt6_uncached_list_add(rt);
rcu_read_unlock();
@@ -2648,8 +2649,7 @@ struct dst_entry *ip6_route_output_flags(struct net *net,
rcu_read_lock();
dst = ip6_route_output_flags_noref(net, sk, fl6, flags);
rt6 = dst_rt6_info(dst);
- /* For dst cached in uncached_list, refcnt is already taken. */
- if (list_empty(&rt6->dst.rt_uncached) && !dst_hold_safe(dst)) {
+ if (!rt6->rt6i_count_held && !dst_hold_safe(dst)) {
dst = &net->ipv6.ip6_null_entry->dst;
dst_hold(dst);
}
ip6_pol_route() can return a dst entry with elevated reference count even when the caller ask for the RT6_LOOKUP_F_DST_NOREF flag. Currently the caller uses the rt_uncached list entry field to detect such scenario: the reference is elevated only for entry in the uncached list. Soon we are going to insert in the uncached list even entry held by the dst_cache(s), potentially fooling the above check and causing reference underflow. To avoid such issue, introduce and use a new field to mark the entries with refcount elevated. No functional change intended. Before: pahole -EC rt6_info /* size: 224, cachelines: 4, members: 9 */ /* sum members: 218, holes: 1, sum holes: 4 */ After: pahole: -EC rt6_info /* size: 224, cachelines: 4, members: 10 */ /* sum members: 219, holes: 1, sum holes: 4 */ Signed-off-by: Paolo Abeni <pabeni@redhat.com> --- include/net/ip6_fib.h | 3 +++ net/ipv6/route.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-)