Module Name: src Committed By: ozaki-r Date: Thu Dec 14 05:47:45 UTC 2017
Modified Files: src/sys/net: rtsock.c Log Message: Protect ifp returned from route_output_get_ifa surely An ifp returned from route_output_get_ifa was supposed to be protected by a returned ifa; if the ifa belongs to ifp, holding the ifa prevents the ifp from being freed. However route_output_get_ifa can return an ifp to which a returned ifa doesn't belong. So we need to take a reference to a returning ifp separately. To generate a diff of this commit: cvs rdiff -u -r1.232 -r1.233 src/sys/net/rtsock.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/rtsock.c diff -u src/sys/net/rtsock.c:1.232 src/sys/net/rtsock.c:1.233 --- src/sys/net/rtsock.c:1.232 Thu Dec 14 05:43:14 2017 +++ src/sys/net/rtsock.c Thu Dec 14 05:47:45 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: rtsock.c,v 1.232 2017/12/14 05:43:14 ozaki-r Exp $ */ +/* $NetBSD: rtsock.c,v 1.233 2017/12/14 05:47:45 ozaki-r Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -61,7 +61,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: rtsock.c,v 1.232 2017/12/14 05:43:14 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rtsock.c,v 1.233 2017/12/14 05:47:45 ozaki-r Exp $"); #ifdef _KERNEL_OPT #include "opt_inet.h" @@ -608,7 +608,7 @@ route_output_report(struct rtentry *rt, static struct ifaddr * __noinline route_output_get_ifa(const struct rt_addrinfo info, const struct rtentry *rt, - struct ifnet **ifp, struct psref *psref) + struct ifnet **ifp, struct psref *psref_ifp, struct psref *psref) { struct ifaddr *ifa = NULL; @@ -618,6 +618,7 @@ route_output_get_ifa(const struct rt_add if (ifa == NULL) goto next; *ifp = ifa->ifa_ifp; + if_acquire(*ifp, psref_ifp); if (info.rti_info[RTAX_IFA] == NULL && info.rti_info[RTAX_GATEWAY] == NULL) goto next; @@ -648,8 +649,14 @@ next: info.rti_info[RTAX_GATEWAY], psref); } out: - if (ifa != NULL && *ifp == NULL) + if (ifa != NULL && *ifp == NULL) { *ifp = ifa->ifa_ifp; + if_acquire(*ifp, psref_ifp); + } + if (ifa == NULL && *ifp != NULL) { + if_put(*ifp, psref_ifp); + *ifp = NULL; + } return ifa; } @@ -660,7 +667,7 @@ route_output_change(struct rtentry *rt, int error = 0; struct ifnet *ifp = NULL, *new_ifp; struct ifaddr *ifa = NULL, *new_ifa; - struct psref psref_ifa, psref_new_ifa, psref_ifp; + struct psref psref_ifa, psref_new_ifa, psref_ifp, psref_new_ifp; bool newgw, ifp_changed = false; /* @@ -674,6 +681,7 @@ route_output_change(struct rtentry *rt, if (newgw || info->rti_info[RTAX_IFP] != NULL || info->rti_info[RTAX_IFA] != NULL) { ifp = rt_getifp(info, &psref_ifp); + /* info refers ifp so we need to keep a reference */ ifa = rt_getifa(info, &psref_ifa); if (ifa == NULL) { error = ENETUNREACH; @@ -698,7 +706,8 @@ route_output_change(struct rtentry *rt, * flags may also be different; ifp may be specified * by ll sockaddr when protocol address is ambiguous */ - new_ifa = route_output_get_ifa(*info, rt, &new_ifp, &psref_new_ifa); + new_ifa = route_output_get_ifa(*info, rt, &new_ifp, &psref_new_ifp, + &psref_new_ifa); if (new_ifa != NULL) { ifa_release(ifa, &psref_ifa); ifa = new_ifa; @@ -736,6 +745,7 @@ route_output_change(struct rtentry *rt, (void)ifp_changed; /* XXX gcc */ #endif out: + if_put(new_ifp, &psref_new_ifp); if_put(ifp, &psref_ifp); return error;