Rewrite nd6_sysctl_{d,p}rlist() to avoid misaligned accesses to char arrays

casted to structs by getting rid of these buffers entirely. In r169832, it
was tried to paper over this issue by 32-bit aligning the buffers. Depending
on compiler optimizations that still was insufficient for 64-bit architectures
with strong alignment requirements though.
While at it, add comments regarding the total lack of locking in this area.

Tested by:	bz
Reviewed by:	bz (slightly earlier version), yongari (earlier version)
MFC after:	1 week
This commit is contained in:
Marius Strobl 2012-05-20 05:12:31 +00:00
parent ba5c15d9ba
commit 7195094c3c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=235681

View File

@ -2262,128 +2262,101 @@ SYSCTL_VNET_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXQLEN, nd6_maxqueuelen,
static int
nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
{
int error;
char buf[1024] __aligned(4);
struct in6_defrouter *d, *de;
struct in6_defrouter d;
struct nd_defrouter *dr;
int error;
if (req->newptr)
return EPERM;
error = 0;
return (EPERM);
bzero(&d, sizeof(d));
d.rtaddr.sin6_family = AF_INET6;
d.rtaddr.sin6_len = sizeof(d.rtaddr);
/*
* XXX locking
*/
TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) {
d = (struct in6_defrouter *)buf;
de = (struct in6_defrouter *)(buf + sizeof(buf));
if (d + 1 <= de) {
bzero(d, sizeof(*d));
d->rtaddr.sin6_family = AF_INET6;
d->rtaddr.sin6_len = sizeof(d->rtaddr);
d->rtaddr.sin6_addr = dr->rtaddr;
error = sa6_recoverscope(&d->rtaddr);
if (error != 0)
return (error);
d->flags = dr->flags;
d->rtlifetime = dr->rtlifetime;
d->expire = dr->expire;
d->if_index = dr->ifp->if_index;
} else
panic("buffer too short");
error = SYSCTL_OUT(req, buf, sizeof(*d));
if (error)
break;
d.rtaddr.sin6_addr = dr->rtaddr;
error = sa6_recoverscope(&d.rtaddr);
if (error != 0)
return (error);
d.flags = dr->flags;
d.rtlifetime = dr->rtlifetime;
d.expire = dr->expire;
d.if_index = dr->ifp->if_index;
error = SYSCTL_OUT(req, &d, sizeof(d));
if (error != 0)
return (error);
}
return (error);
return (0);
}
static int
nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
{
int error;
char buf[1024] __aligned(4);
struct in6_prefix *p, *pe;
struct in6_prefix p;
struct sockaddr_in6 s6;
struct nd_prefix *pr;
struct nd_pfxrouter *pfr;
time_t maxexpire;
int error;
char ip6buf[INET6_ADDRSTRLEN];
if (req->newptr)
return EPERM;
error = 0;
return (EPERM);
bzero(&p, sizeof(p));
p.origin = PR_ORIG_RA;
bzero(&s6, sizeof(s6));
s6.sin6_family = AF_INET6;
s6.sin6_len = sizeof(s6);
/*
* XXX locking
*/
LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
u_short advrtrs;
size_t advance;
struct sockaddr_in6 *sin6, *s6;
struct nd_pfxrouter *pfr;
p = (struct in6_prefix *)buf;
pe = (struct in6_prefix *)(buf + sizeof(buf));
if (p + 1 <= pe) {
bzero(p, sizeof(*p));
sin6 = (struct sockaddr_in6 *)(p + 1);
p->prefix = pr->ndpr_prefix;
if (sa6_recoverscope(&p->prefix)) {
p.prefix = pr->ndpr_prefix;
if (sa6_recoverscope(&p.prefix)) {
log(LOG_ERR, "scope error in prefix list (%s)\n",
ip6_sprintf(ip6buf, &p.prefix.sin6_addr));
/* XXX: press on... */
}
p.raflags = pr->ndpr_raf;
p.prefixlen = pr->ndpr_plen;
p.vltime = pr->ndpr_vltime;
p.pltime = pr->ndpr_pltime;
p.if_index = pr->ndpr_ifp->if_index;
if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
p.expire = 0;
else {
/* XXX: we assume time_t is signed. */
maxexpire = (-1) &
~((time_t)1 << ((sizeof(maxexpire) * 8) - 1));
if (pr->ndpr_vltime < maxexpire - pr->ndpr_lastupdate)
p.expire = pr->ndpr_lastupdate +
pr->ndpr_vltime;
else
p.expire = maxexpire;
}
p.refcnt = pr->ndpr_refcnt;
p.flags = pr->ndpr_stateflags;
p.advrtrs = 0;
LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry)
p.advrtrs++;
error = SYSCTL_OUT(req, &p, sizeof(p));
if (error != 0)
return (error);
LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) {
s6.sin6_addr = pfr->router->rtaddr;
if (sa6_recoverscope(&s6))
log(LOG_ERR,
"scope error in prefix list (%s)\n",
ip6_sprintf(ip6buf, &p->prefix.sin6_addr));
/* XXX: press on... */
}
p->raflags = pr->ndpr_raf;
p->prefixlen = pr->ndpr_plen;
p->vltime = pr->ndpr_vltime;
p->pltime = pr->ndpr_pltime;
p->if_index = pr->ndpr_ifp->if_index;
if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
p->expire = 0;
else {
time_t maxexpire;
/* XXX: we assume time_t is signed. */
maxexpire = (-1) &
~((time_t)1 <<
((sizeof(maxexpire) * 8) - 1));
if (pr->ndpr_vltime <
maxexpire - pr->ndpr_lastupdate) {
p->expire = pr->ndpr_lastupdate +
pr->ndpr_vltime;
} else
p->expire = maxexpire;
}
p->refcnt = pr->ndpr_refcnt;
p->flags = pr->ndpr_stateflags;
p->origin = PR_ORIG_RA;
advrtrs = 0;
LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) {
if ((void *)&sin6[advrtrs + 1] > (void *)pe) {
advrtrs++;
continue;
}
s6 = &sin6[advrtrs];
bzero(s6, sizeof(*s6));
s6->sin6_family = AF_INET6;
s6->sin6_len = sizeof(*sin6);
s6->sin6_addr = pfr->router->rtaddr;
if (sa6_recoverscope(s6)) {
log(LOG_ERR,
"scope error in "
"prefix list (%s)\n",
ip6_sprintf(ip6buf,
&pfr->router->rtaddr));
}
advrtrs++;
}
p->advrtrs = advrtrs;
} else
panic("buffer too short");
advance = sizeof(*p) + sizeof(*sin6) * advrtrs;
error = SYSCTL_OUT(req, buf, advance);
if (error)
break;
ip6_sprintf(ip6buf, &pfr->router->rtaddr));
error = SYSCTL_OUT(req, &s6, sizeof(s6));
if (error != 0)
return (error);
}
}
return (error);
return (0);
}