| Summary: | "ip_dooptions()" might dereference unaligned pointer | ||
|---|---|---|---|
| Product: | Base System | Reporter: | guy |
| Component: | alpha | Assignee: | jlemon |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | 1.0-RELEASE | ||
| Hardware: | Any | ||
| OS: | Any | ||
Responsible Changed From-To: freebsd-alpha->jlemon fixed in -current, PR left open until fix MFC'd to -stable. State Changed From-To: open->closed Fix committed. |
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. 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. 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.