Subject: kern/29199: msghdr parsing incorrect in kernel for v6 options
To: None <,,>
From: None <>
List: netbsd-bugs
Date: 02/02/2005 15:12:00
>Number:         29199
>Category:       kern
>Synopsis:       msghdr parsing incorrect in kernel for v6 options
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Feb 02 15:12:00 +0000 2005
>Originator:     Greg Troxel
>Release:        NetBSD 2.0_RC4
        Greg Troxel <>
System: NetBSD 2.0_RC4 NetBSD 2.0_RC4 (SINEW) #44: Mon Jan 10 19:54:05 EST 2005 i386
Architecture: i386
Machine: i386
In netinet/ip6_output:ip6_setpktoptions, an mbuf with control data is
looped over to parse all the options.  However, the code assumes that
the mbuf is sized appropriately for the data, and can crash the system
if bad data is passed by the user.

	for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
	    control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
		cm = mtod(control, struct cmsghdr *);
		if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
			return (EINVAL);

There are two problems here.  One is that no check is made to prevent
control->m_len from going negative, since it is quite possible that
the mbuf is only CMSG_LEN rather than CMSG_SPACE for the data.  The
second is that it is not verified that the entire header is present
before the mtod and dereferencing of cm.

The code does check for cm->cmsg_len > control->m_len, but it's
possible for that to fail but still have CMSG_ALIGN(cm->cmsg_len) be
greater, for example if cmsg_len is 20 on sparc.


While debugging quagga's sending of router advertisements, I caused
crashes on both sparc and sparc64, I think 1.6.2.  My memory is
slightly fuzzy and I hvae lost the kernel patch, but the problem was
sending a control message with the length being CMSG_LEN.  I then
changed to use SPACE, as rtadvd does.

Inspect source code, and see that the above conditions are possible.


(not tested, from memory)

Add at start of for body:

if (cm != NULL && control->m_data == CMSG_LEN(cm->cmsg_len))
	/* Control message exactly filled mbuf. */
if (control->m_len < sizeof(struct cmsghdr))
	/* Junk, other than proper padding, at end of control message. */
	return (EINVAL);

Or, move iteration step to end of for, and check for exact match and
overrun there.