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;

Reply via email to