From nobody@FreeBSD.org  Tue Oct  2 00:50:06 2001
Return-Path: <nobody@FreeBSD.org>
Received: from freefall.freebsd.org (freefall.FreeBSD.org [216.136.204.21])
	by hub.freebsd.org (Postfix) with ESMTP id 2625437B408
	for <freebsd-gnats-submit@FreeBSD.org>; Tue,  2 Oct 2001 00:50:02 -0700 (PDT)
Received: (from nobody@localhost)
	by freefall.freebsd.org (8.11.4/8.11.4) id f927o2g43335;
	Tue, 2 Oct 2001 00:50:02 -0700 (PDT)
	(envelope-from nobody)
Message-Id: <200110020750.f927o2g43335@freefall.freebsd.org>
Date: Tue, 2 Oct 2001 00:50:02 -0700 (PDT)
From: Guy Harris <guy@alum.mit.edu>
To: freebsd-gnats-submit@FreeBSD.org
Subject: "ip_dooptions()" might dereference unaligned pointer
X-Send-Pr-Version: www-1.0

>Number:         30982
>Category:       alpha
>Synopsis:       "ip_dooptions()" might dereference unaligned pointer
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    jlemon
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Oct 02 01:00:00 PDT 2001
>Closed-Date:    Fri Nov 2 08:51:07 PST 2001
>Last-Modified:  Fri Nov 02 08:51:31 PST 2001
>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] - (int)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;
			}
			sin = (struct in_addr *)(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 = (INA)ifaof_ifpforaddr((SA)&ipaddr,
							    m->m_pkthdr.rcvif);
				if (ia == 0)
					continue;
				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
				    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;
				}
				memcpy(&ipaddr.sin_addr, sin,
				    sizeof(struct in_addr));
				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
					continue;
				cp[IPOPT_OFFSET] += sizeof(struct in_addr);
				break;

			default:
				code = &cp[3] - (u_char *)ip;
				goto bad;
			}
			ntime = iptime();
			(void)memcpy((caddr_t)cp + cp[IPOPT_OFFSET] - 1, &ntime,
			    sizeof(n_time));
			cp[IPOPT_OFFSET] += sizeof(n_time);

should, I think, do it.
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-alpha->jlemon 
Responsible-Changed-By: jlemon 
Responsible-Changed-When: Wed Oct 24 23:28:05 PDT 2001 
Responsible-Changed-Why:  
fixed in -current, PR left open until fix MFC'd to -stable. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=30982 
State-Changed-From-To: open->closed 
State-Changed-By: jlemon 
State-Changed-When: Fri Nov 2 08:51:07 PST 2001 
State-Changed-Why:  
Fix committed. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=30982 
>Unformatted:
