Subject: kern/14124: "ip_dooptions()" might dereference unaligned pointer on Alpha
To: None <gnats-bugs@gnats.netbsd.org>
From: None <guy@alum.mit.edu>
List: netbsd-bugs
Date: 10/02/2001 00:57:15
>Number: 14124
>Category: kern
>Synopsis: "ip_dooptions()" might dereference unaligned pointer on Alpha
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue Oct 02 00:58:00 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator: Guy Harris
>Release:
>Organization:
>Environment:
>Description:
At Network Appliance, we have a BSD-derived networking stack; one of
our Alpha-based machines crashed due to an incoming IP packet that
had a 15-byte Record Route option followed by a Timestamp option.
The code that processes the Timestamp option casts the pointer to the
beginning of the option to a pointer to a "struct ip_timestamp" and
dereferences that pointer.
The only fields it fetches or sets via that pointer are one-byte
fields; however, at least with the version of GCC we are using
at NetApp, the code the compiler generates to fetch from and store
into those one-byte fields assumes that the structure is aligned
on a 4-byte boundary. (We don't tell the compiler to generate code
to use the BWX extensions, so it generates loads and extracts, and
it generates a load rather than a "load unaligned".)
This meant that the code attempted to dereference an unaligned pointer,
as the 15-byte Record Route option put the next option on an odd-byte
boundary.
>How-To-Repeat:
If the generated Alpha kernel code does an aligned load, send to
an Alpha machine a packet with a Record Route option (which should
contain an odd number of bytes) followed immediately (with no padding)
by a Timestamp option.
>Fix:
Changing the code that processes time stamp options to
code = cp - (u_char *)ip;
if (cp[IPOPT_OLEN] < 4 || cp[IPOPT_OLEN] > 40) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
if (cp[IPOPT_OFFSET] < 5) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
if (cp[IPOPT_OFFSET] > cp[IPOPT_OLEN] - sizeof(int32_t)) {
/* Increment the overflow counter. */
cp[3] += 0x10;
if ((cp[3] & 0xF0) == 0) {
/* The overflow counter overflowed. */
code = &cp[IPOPT_OFFSET] -
(u_char *)ip;
goto bad;
}
break;
}
cp0 = (cp + cp[IPOPT_OFFSET] - 1);
switch (cp[3] & 0x0F) {
case IPOPT_TS_TSONLY:
break;
case IPOPT_TS_TSANDADDR:
if (cp[IPOPT_OFFSET] - 1 + sizeof(n_time) +
sizeof(struct in_addr) > cp[IPOPT_OLEN]) {
code = &cp[IPOPT_OFFSET] -
(u_char *)ip;
goto bad;
}
ipaddr.sin_addr = dst;
ia = ifatoia(ifaof_ifpforaddr(sintosa(&ipaddr),
m->m_pkthdr.rcvif));
if (ia == 0)
continue;
bcopy(ia->ia_addr.sin_addr,
cp0, sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
break;
case IPOPT_TS_PRESPEC:
if (cp[IPOPT_OFFSET] - 1 + sizeof(n_time) +
sizeof(struct in_addr) > cp[IPOPT_OLEN]) {
code = &cp[IPOPT_OFFSET] -
(u_char *)ip;
goto bad;
}
bcopy(cp0, &ipaddr.sin_addr,
sizeof(struct in_addr));
if (ifatoia(ifa_ifwithaddr(sintosa(&ipaddr)))
== NULL)
continue;
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
break;
default:
code = &cp[3] - (u_char *)ip;
goto bad;
}
ntime = iptime();
cp0 = (u_char *) &ntime; /* XXX grumble, GCC... */
bcopy(cp0, (caddr_t)cp + cp[IPOPT_OFFSET] - 1,
sizeof(n_time));
cp[IPOPT_OFFSET] += sizeof(n_time);
should, I think, do it.
>Release-Note:
>Audit-Trail:
>Unformatted: