After multiple ifconfig create and destroy of bridge and tun interfaces rtadvd crashed just seconds after being started. I had 7 interfaces, but tun0 had ifm_index == 16. rtadvd allocated insufficient space for iflist. iflist got initialized but then it's tail got overwritten. This diff determines the maximum ifm_index (instead of "roughly estimate") and allocates the correct number of bytes.
Index: if.c =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/if.c,v retrieving revision 1.23 diff -u -r1.23 if.c --- if.c 21 May 2010 13:41:23 -0000 1.23 +++ if.c 28 May 2011 11:16:36 -0000 @@ -455,20 +455,11 @@ static void parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize) { - int iflentry_size, malloc_size; + int malloc_size; struct if_msghdr *ifm; struct ifa_msghdr *ifam; char *lim; - - /* - * Estimate least size of an iflist entry, to be obtained from kernel. - * Should add sizeof(sockaddr) ?? - */ - iflentry_size = sizeof(struct if_msghdr); - /* roughly estimate max list size of pointers to each if_msghdr */ - malloc_size = (bufsize/iflentry_size) * sizeof(size_t); - if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) - fatal("malloc"); + int max_ifm_index = 0; lim = buf + bufsize; for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { @@ -479,7 +470,8 @@ } if (ifm->ifm_type == RTM_IFINFO) { if (ifm->ifm_version == RTM_VERSION) - (*ifmlist_p)[ifm->ifm_index] = ifm; + if (ifm->ifm_index > max_ifm_index) + max_ifm_index = ifm->ifm_index; } else { log_warn("out of sync parsing NET_RT_IFLIST," " expected %d, got %d, msglen = %d," @@ -487,6 +479,34 @@ RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, buf, ifm, lim); exit(1); + } + for (ifam = (struct ifa_msghdr *) + ((char *)ifm + ifm->ifm_msglen); + ifam < (struct ifa_msghdr *)lim; + ifam = (struct ifa_msghdr *) + ((char *)ifam + ifam->ifam_msglen)) { + /* just for safety */ + if (!ifam->ifam_msglen) { + log_warnx("ifa_msglen is 0 " + "(buf=%p lim=%p ifam=%p)", + buf, lim, ifam); + return; + } + if (ifam->ifam_type != RTM_NEWADDR) + break; + } + ifm = (struct if_msghdr *)ifam; + } + + malloc_size = (max_ifm_index + 1) * sizeof(size_t); + if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) + fatal("malloc"); + + for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { + if (ifm->ifm_type == RTM_IFINFO) { + if (ifm->ifm_version == RTM_VERSION) { + (*ifmlist_p)[ifm->ifm_index] = ifm; + } } for (ifam = (struct ifa_msghdr *) ((char *)ifm + ifm->ifm_msglen); -- Michal Mazurek