Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753065AbdHRNQz (ORCPT ); Fri, 18 Aug 2017 09:16:55 -0400 Received: from shadbolt.e.decadent.org.uk ([88.96.1.126]:55770 "EHLO shadbolt.e.decadent.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752952AbdHRNPz (ORCPT ); Fri, 18 Aug 2017 09:15:55 -0400 Content-Type: text/plain; charset="UTF-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 From: Ben Hutchings To: linux-kernel@vger.kernel.org, stable@vger.kernel.org CC: akpm@linux-foundation.org, "David S. Miller" , "Eric Dumazet" , "Andrey Konovalov" , "WANG Cong" Date: Fri, 18 Aug 2017 14:13:20 +0100 Message-ID: X-Mailer: LinuxStableQueue (scripts by bwh) Subject: [PATCH 3.16 119/134] ipv4: restore rt->fi for reference counting In-Reply-To: X-SA-Exim-Connect-IP: 82.70.136.246 X-SA-Exim-Mail-From: ben@decadent.org.uk X-SA-Exim-Scanned: No (on shadbolt.decadent.org.uk); SAEximRunCond expanded to false Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 3895 Lines: 127 3.16.47-rc1 review patch. If anyone has any objections, please let me know. ------------------ From: WANG Cong commit 82486aa6f1b9bc8145e6d0fa2bc0b44307f3b875 upstream. IPv4 dst could use fi->fib_metrics to store metrics but fib_info itself is refcnt'ed, so without taking a refcnt fi and fi->fib_metrics could be freed while dst metrics still points to it. This triggers use-after-free as reported by Andrey twice. This patch reverts commit 2860583fe840 ("ipv4: Kill rt->fi") to restore this reference counting. It is a quick fix for -net and -stable, for -net-next, as Eric suggested, we can consider doing reference counting for metrics itself instead of relying on fib_info. IPv6 is very different, it copies or steals the metrics from mx6_config in fib6_commit_metrics() so probably doesn't need a refcnt. Decnet has already done the refcnt'ing, see dn_fib_semantic_match(). Fixes: 2860583fe840 ("ipv4: Kill rt->fi") Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: Cong Wang Acked-by: Eric Dumazet Signed-off-by: David S. Miller [bwh: Backported to 3.16: - Update all 5 places where rtable is initialised - Open-code fib_info_hold() - Adjust context] Signed-off-by: Ben Hutchings --- --- a/include/net/route.h +++ b/include/net/route.h @@ -64,6 +64,7 @@ struct rtable { u32 rt_pmtu; struct list_head rt_uncached; + struct fib_info *fi; /* for refcnt to shared metrics */ }; static inline bool rt_is_input_route(const struct rtable *rt) --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1346,6 +1346,11 @@ static void ipv4_dst_destroy(struct dst_ { struct rtable *rt = (struct rtable *) dst; + if (rt->fi) { + fib_info_put(rt->fi); + rt->fi = NULL; + } + if (!list_empty(&rt->rt_uncached)) { spin_lock_bh(&rt_uncached_lock); list_del(&rt->rt_uncached); @@ -1378,6 +1383,16 @@ static bool rt_cache_valid(const struct !rt_is_expired(rt); } +static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) +{ + if (fi->fib_metrics != (u32 *)dst_default_metrics) { + atomic_inc(&fi->fib_clntref); + rt->fi = fi; + } + + dst_init_metrics(&rt->dst, fi->fib_metrics, true); +} + static void rt_set_nexthop(struct rtable *rt, __be32 daddr, const struct fib_result *res, struct fib_nh_exception *fnhe, @@ -1392,7 +1407,7 @@ static void rt_set_nexthop(struct rtable rt->rt_gateway = nh->nh_gw; rt->rt_uses_gateway = 1; } - dst_init_metrics(&rt->dst, fi->fib_metrics, true); + rt_init_metrics(rt, fi); #ifdef CONFIG_IP_ROUTE_CLASSID rt->dst.tclassid = nh->nh_tclassid; #endif @@ -1480,6 +1495,7 @@ static int ip_route_input_mc(struct sk_b rth->rt_pmtu = 0; rth->rt_gateway = 0; rth->rt_uses_gateway = 0; + rth->fi = NULL; INIT_LIST_HEAD(&rth->rt_uncached); if (our) { rth->dst.input= ip_local_deliver; @@ -1649,6 +1665,7 @@ rt_cache: rth->rt_pmtu = 0; rth->rt_gateway = 0; rth->rt_uses_gateway = 0; + rth->fi = NULL; INIT_LIST_HEAD(&rth->rt_uncached); RT_CACHE_STAT_INC(in_slow_tot); @@ -1823,6 +1840,7 @@ local_input: rth->rt_pmtu = 0; rth->rt_gateway = 0; rth->rt_uses_gateway = 0; + rth->fi = NULL; INIT_LIST_HEAD(&rth->rt_uncached); RT_CACHE_STAT_INC(in_slow_tot); if (res.type == RTN_UNREACHABLE) { @@ -2037,6 +2055,7 @@ add: rth->rt_pmtu = 0; rth->rt_gateway = 0; rth->rt_uses_gateway = 0; + rth->fi = NULL; INIT_LIST_HEAD(&rth->rt_uncached); RT_CACHE_STAT_INC(out_slow_tot); @@ -2316,6 +2335,9 @@ struct dst_entry *ipv4_blackhole_route(s rt->rt_type = ort->rt_type; rt->rt_gateway = ort->rt_gateway; rt->rt_uses_gateway = ort->rt_uses_gateway; + rt->fi = ort->fi; + if (rt->fi) + atomic_inc(&rt->fi->fib_clntref); INIT_LIST_HEAD(&rt->rt_uncached);