kern/149240: struct ifm_data.ifi_datalen set to invalid value on 32-bit access to 64-bit kernel

Stef Walter stef at
Tue Aug 3 15:10:04 UTC 2010

>Number:         149240
>Category:       kern
>Synopsis:       struct ifm_data.ifi_datalen set to invalid value on 32-bit access to 64-bit kernel
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Aug 03 15:10:03 UTC 2010
>Originator:     Stef Walter
>Release:        FreeBSD 8.1
FreeBSD 8.1-RELEASE FreeBSD 8.1-RELEASE #4: Tue Aug  3 14:31:11 UTC 2010     root at  amd64
The FreeBSD 32 compatibility layer sets the struct ifm_data.ifi_datalen member to the wrong value. ifi_datalen is supposed to contain the size of the ifm_data struct. The FreeBSD 32 compatibility code sets this to the sime of the 64-bit struct ifi_data rather than the 32-bit one.

This casues 32-bit applications using the sysctl() or PF_ROUTE style interfaces to access invalid memory, produce invalid results, and in many cases crash.
/* Will produce invalid interface names when run as a 32-bit program on 64-bit */
/* gcc -o test_iflist test_iflist.c */

#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>

#include <string.h>

main (void)
	char buffer[1024 * 64];
	char name[IFNAMSIZ];
	char *buf, *lim, *next;
	int mib[6];
	size_t length;
	struct if_msghdr *ifm, *nextifm;
	struct ifa_msghdr *ifam;
	struct sockaddr_dl *sdl;
	int addrcount;
	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;
	mib[3] = 0;                     /* address family */
	mib[4] = NET_RT_IFLIST;
	mib[5] = 0;

	length = sizeof (buffer);
	if (sysctl (mib, 6, buffer, &length, NULL, 0) < 0)
		err (1, "sysctl failed");

	buf = next = buffer;
	lim = buf + length;

	while (next < lim) {
		ifm = (struct if_msghdr*)next;
		if (ifm->ifm_type != RTM_IFINFO)
			errx (1, "invalid ifm_type");

		if (ifm->ifm_data.ifi_datalen == 0)
			ifm->ifm_data.ifi_datalen = sizeof(struct if_data);
		sdl = (struct sockaddr_dl *)((char *)ifm + 
			sizeof(struct if_msghdr) -
			sizeof(struct if_data) +

		if (sdl->sdl_nlen >= sizeof (name))
			err (1, "name too long");
		strncpy (name, sdl->sdl_data, sdl->sdl_nlen);
		next += ifm->ifm_msglen;
		warnx ("interface %d %s", ifm->ifm_index, name);	

                ifam = NULL;
                addrcount = 0;
                while (next < lim) {
                        nextifm = (struct if_msghdr*)next;
                        if (nextifm->ifm_type != RTM_NEWADDR)
                        if (ifam == NULL)
                                ifam = (struct ifa_msghdr *)nextifm;
                        next += nextifm->ifm_msglen;

	return 0;
Patch attached

Patch attached with submission follows:

--- ./sys/net/rtsock.c.orig	2010-08-03 14:19:14.000000000 +0000
+++ ./sys/net/rtsock.c	2010-08-03 14:20:54.000000000 +0000
@@ -1440,5 +1440,5 @@ copy_ifdata32(struct if_data *src, struc
 	CP(*src, *dst, ifi_hdrlen);
 	CP(*src, *dst, ifi_link_state);
-	CP(*src, *dst, ifi_datalen);
+	dst->ifi_datalen = sizeof(struct if_data32);
 	CP(*src, *dst, ifi_mtu);
 	CP(*src, *dst, ifi_metric);


More information about the freebsd-bugs mailing list