fix interface address list traversal in if_detach():

The code was assuming that interface addresses are removed one-by-one.
With IPv6 and multicasts, removal of one address can remove other
addresses as side effect, which caused accesses of free()d memory.
This commit is contained in:
drochner 2003-11-11 20:33:46 +00:00
parent 79bf8521a5
commit 14332cf998
1 changed files with 9 additions and 6 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: if.c,v 1.133 2003/11/10 20:03:29 jonathan Exp $ */ /* $NetBSD: if.c,v 1.134 2003/11/11 20:33:46 drochner Exp $ */
/*- /*-
* Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc. * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@ -97,7 +97,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.133 2003/11/10 20:03:29 jonathan Exp $"); __KERNEL_RCSID(0, "$NetBSD: if.c,v 1.134 2003/11/11 20:33:46 drochner Exp $");
#include "opt_inet.h" #include "opt_inet.h"
@ -544,7 +544,7 @@ if_detach(ifp)
struct ifnet *ifp; struct ifnet *ifp;
{ {
struct socket so; struct socket so;
struct ifaddr *ifa, *next; struct ifaddr *ifa, **ifap;
#ifdef IFAREF_DEBUG #ifdef IFAREF_DEBUG
struct ifaddr *last_ifa = NULL; struct ifaddr *last_ifa = NULL;
#endif #endif
@ -581,8 +581,8 @@ if_detach(ifp)
* Rip all the addresses off the interface. This should make * Rip all the addresses off the interface. This should make
* all of the routes go away. * all of the routes go away.
*/ */
for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa; ifa = next) { ifap = &TAILQ_FIRST(&ifp->if_addrlist); /* XXX abstraction violation */
next = TAILQ_NEXT(ifa, ifa_list); while ((ifa = *ifap)) {
family = ifa->ifa_addr->sa_family; family = ifa->ifa_addr->sa_family;
#ifdef IFAREF_DEBUG #ifdef IFAREF_DEBUG
printf("if_detach: ifaddr %p, family %d, refcnt %d\n", printf("if_detach: ifaddr %p, family %d, refcnt %d\n",
@ -591,8 +591,10 @@ if_detach(ifp)
panic("if_detach: loop detected"); panic("if_detach: loop detected");
last_ifa = ifa; last_ifa = ifa;
#endif #endif
if (family == AF_LINK) if (family == AF_LINK) {
ifap = &TAILQ_NEXT(ifa, ifa_list);
continue; continue;
}
dp = pffinddomain(family); dp = pffinddomain(family);
#ifdef DIAGNOSTIC #ifdef DIAGNOSTIC
if (dp == NULL) if (dp == NULL)
@ -617,6 +619,7 @@ if_detach(ifp)
*/ */
printf("if_detach: WARNING: AF %d not purged\n", printf("if_detach: WARNING: AF %d not purged\n",
family); family);
TAILQ_REMOVE(&ifp->if_addrlist, ifa, ifa_list);
} }
} }