On 20/08/15(Thu) 17:48, Christian Weisgerber wrote:
> Currently, ndp -p will trigger unaligned accesses in the kernel and
> userland on architectures that require strict alignment for 64-bit
> types (e.g. sparc64).
> 
> This is actually fallout from the time_t change.
> 
> The sysctl used to pass the prefix list uses a packed format:
>   one struct in6_prefix, several struct sockaddr_in6,
>   one struct in6_prefix, several struct sockaddr_in6,
>   ...
> This worked dandily when both structs had the same alignment
> requirement (int32_t).  However, there's a time_t in in6_prefix
> which pushed that struct's alignment to int64_t... boom!
> 
> The patches below are taken from NetBSD (Martin Husemann):
> http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/netinet6/nd6.c.diff?r1=1.145&r2=1.146
> http://cvsweb.netbsd.org/bsdweb.cgi/src/usr.sbin/ndp/ndp.c.diff?r1=1.41&r2=1.42
> 
> Use simple byte pointer arithmetic and memcpy from/to aligned stack
> variables to handle the "packed" binary format passed out to userland
> when querying the prefix/router list.
> 
> OK?

ok mpi@

> 
> Index: sys/netinet6/nd6.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/nd6.c,v
> retrieving revision 1.143
> diff -u -p -r1.143 nd6.c
> --- sys/netinet6/nd6.c        16 Jul 2015 15:31:35 -0000      1.143
> +++ sys/netinet6/nd6.c        20 Aug 2015 14:36:19 -0000
> @@ -1877,45 +1877,43 @@ fill_prlist(void *oldp, size_t *oldlenp,
>  {
>       int error = 0, s;
>       struct nd_prefix *pr;
> -     struct in6_prefix *p = NULL;
> -     struct in6_prefix *pe = NULL;
> +     char *p = NULL, *ps = NULL;
> +     char *pe = NULL;
>       size_t l;
>  
>       s = splsoftnet();
>  
>       if (oldp) {
> -             p = (struct in6_prefix *)oldp;
> -             pe = (struct in6_prefix *)((caddr_t)oldp + *oldlenp);
> +             ps = p = (char *)oldp;
> +             pe = (char *)oldp + *oldlenp;
>       }
>       l = 0;
>  
>       LIST_FOREACH(pr, &nd_prefix, ndpr_entry) {
>               u_short advrtrs;
> -             size_t advance;
> -             struct sockaddr_in6 *sin6;
> -             struct sockaddr_in6 *s6;
> +             struct sockaddr_in6 sin6;
>               struct nd_pfxrouter *pfr;
> +             struct in6_prefix pfx;
>               char addr[INET6_ADDRSTRLEN];
>  
> -             if (oldp && p + 1 <= pe)
> -             {
> -                     bzero(p, sizeof(*p));
> -                     sin6 = (struct sockaddr_in6 *)(p + 1);
> -
> -                     p->prefix = pr->ndpr_prefix;
> -                     if (in6_recoverscope(&p->prefix,
> -                         &p->prefix.sin6_addr, pr->ndpr_ifp) != 0)
> +             if (oldp && p + sizeof(struct in6_prefix) <= pe) {
> +                     memset(&pfx, 0, sizeof(pfx));
> +                     ps = p;
> +
> +                     pfx.prefix = pr->ndpr_prefix;
> +                     if (in6_recoverscope(&pfx.prefix,
> +                         &pfx.prefix.sin6_addr, pr->ndpr_ifp) != 0)
>                               log(LOG_ERR,
>                                   "scope error in prefix list (%s)\n",
> -                                 inet_ntop(AF_INET6, &p->prefix.sin6_addr,
> +                                 inet_ntop(AF_INET6, &pfx.prefix.sin6_addr,
>                                       addr, sizeof(addr)));
> -                     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;
> +                     pfx.raflags = pr->ndpr_raf;
> +                     pfx.prefixlen = pr->ndpr_plen;
> +                     pfx.vltime = pr->ndpr_vltime;
> +                     pfx.pltime = pr->ndpr_pltime;
> +                     pfx.if_index = pr->ndpr_ifp->if_index;
>                       if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
> -                             p->expire = 0;
> +                             pfx.expire = 0;
>                       else {
>                               time_t maxexpire;
>  
> @@ -1924,40 +1922,44 @@ fill_prlist(void *oldp, size_t *oldlenp,
>                                   ((sizeof(maxexpire) * 8) - 1));
>                               if (pr->ndpr_vltime <
>                                   maxexpire - pr->ndpr_lastupdate) {
> -                                     p->expire = pr->ndpr_lastupdate +
> +                                     pfx.expire = pr->ndpr_lastupdate +
>                                               pr->ndpr_vltime;
>                               } else
> -                                     p->expire = maxexpire;
> +                                     pfx.expire = maxexpire;
>                       }
> -                     p->refcnt = pr->ndpr_refcnt;
> -                     p->flags = pr->ndpr_stateflags;
> -                     p->origin = PR_ORIG_RA;
> +                     pfx.refcnt = pr->ndpr_refcnt;
> +                     pfx.flags = pr->ndpr_stateflags;
> +                     pfx.origin = PR_ORIG_RA;
> +
> +                     p += sizeof(pfx); l += sizeof(pfx);
> +
>                       advrtrs = 0;
>                       LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) {
> -                             if ((void *)&sin6[advrtrs + 1] > (void *)pe) {
> +                             if (p + sizeof(sin6) > pe) {
>                                       advrtrs++;
>                                       continue;
>                               }
> -                             s6 = &sin6[advrtrs];
> -                             s6->sin6_family = AF_INET6;
> -                             s6->sin6_len = sizeof(struct sockaddr_in6);
> -                             s6->sin6_addr = pfr->router->rtaddr;
> -                             in6_recoverscope(s6, &pfr->router->rtaddr,
> +                             sin6.sin6_family = AF_INET6;
> +                             sin6.sin6_len = sizeof(struct sockaddr_in6);
> +                             sin6.sin6_addr = pfr->router->rtaddr;
> +                             in6_recoverscope(&sin6, &pfr->router->rtaddr,
>                                   pfr->router->ifp);
>                               advrtrs++;
> +                             memcpy(p, &sin6, sizeof(sin6));
> +                             p += sizeof(sin6);
> +                             l += sizeof(sin6);
>                       }
> -                     p->advrtrs = advrtrs;
> +                     pfx.advrtrs = advrtrs;
> +                     memcpy(ps, &pfx, sizeof(pfx));
>               }
>               else {
> +                     l += sizeof(pfx);
>                       advrtrs = 0;
> -                     LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry)
> +                     LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) {
>                               advrtrs++;
> +                             l += sizeof(sin6);
> +                     }
>               }
> -
> -             advance = sizeof(*p) + sizeof(*sin6) * advrtrs;
> -             l += advance;
> -             if (p)
> -                     p = (struct in6_prefix *)((caddr_t)p + advance);
>       }
>  
>       if (oldp) {
> Index: usr.sbin/ndp/ndp.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/ndp/ndp.c,v
> retrieving revision 1.61
> diff -u -p -r1.61 ndp.c
> --- usr.sbin/ndp/ndp.c        3 Jun 2015 08:10:53 -0000       1.61
> +++ usr.sbin/ndp/ndp.c        20 Aug 2015 15:09:37 -0000
> @@ -1043,9 +1043,8 @@ void
>  plist(void)
>  {
>       int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST };
> -     char *buf;
> -     struct in6_prefix *p, *ep, *n;
> -     struct sockaddr_in6 *advrtr;
> +     char *buf, *p, *ep;
> +     struct in6_prefix pfx;
>       size_t l;
>       struct timeval now;
>       const int niflags = NI_NUMERICHOST;
> @@ -1066,17 +1065,17 @@ plist(void)
>               /*NOTREACHED*/
>       }
>  
> -     ep = (struct in6_prefix *)(buf + l);
> -     for (p = (struct in6_prefix *)buf; p < ep; p = n) {
> -             advrtr = (struct sockaddr_in6 *)(p + 1);
> -             n = (struct in6_prefix *)&advrtr[p->advrtrs];
> +     ep = buf + l;
> +     for (p = buf; p < ep; ) {
> +             memcpy(&pfx, p, sizeof(pfx));
> +             p += sizeof(pfx);
>  
> -             if (getnameinfo((struct sockaddr *)&p->prefix,
> -                 p->prefix.sin6_len, namebuf, sizeof(namebuf),
> +             if (getnameinfo((struct sockaddr *)&pfx.prefix,
> +                 pfx.prefix.sin6_len, namebuf, sizeof(namebuf),
>                   NULL, 0, niflags) != 0)
>                       strlcpy(namebuf, "?", sizeof(namebuf));
> -             printf("%s/%d if=%s\n", namebuf, p->prefixlen,
> -                 if_indextoname(p->if_index, ifix_buf));
> +             printf("%s/%d if=%s\n", namebuf, pfx.prefixlen,
> +                 if_indextoname(pfx.if_index, ifix_buf));
>  
>               gettimeofday(&now, 0);
>               /*
> @@ -1084,50 +1083,52 @@ plist(void)
>                * by origin.  notify the difference to the users.
>                */
>               printf("flags=%s%s%s%s%s",
> -                 p->raflags.onlink ? "L" : "",
> -                 p->raflags.autonomous ? "A" : "",
> -                 (p->flags & NDPRF_ONLINK) != 0 ? "O" : "",
> -                 (p->flags & NDPRF_DETACHED) != 0 ? "D" : "",
> -                 (p->flags & NDPRF_HOME) != 0 ? "H" : ""
> +                 pfx.raflags.onlink ? "L" : "",
> +                 pfx.raflags.autonomous ? "A" : "",
> +                 (pfx.flags & NDPRF_ONLINK) != 0 ? "O" : "",
> +                 (pfx.flags & NDPRF_DETACHED) != 0 ? "D" : "",
> +                 (pfx.flags & NDPRF_HOME) != 0 ? "H" : ""
>                   );
> -             if (p->vltime == ND6_INFINITE_LIFETIME)
> +             if (pfx.vltime == ND6_INFINITE_LIFETIME)
>                       printf(" vltime=infinity");
>               else
> -                     printf(" vltime=%lu", (unsigned long)p->vltime);
> -             if (p->pltime == ND6_INFINITE_LIFETIME)
> +                     printf(" vltime=%lu", (unsigned long)pfx.vltime);
> +             if (pfx.pltime == ND6_INFINITE_LIFETIME)
>                       printf(", pltime=infinity");
>               else
> -                     printf(", pltime=%lu", (unsigned long)p->pltime);
> -             if (p->expire == 0)
> +                     printf(", pltime=%lu", (unsigned long)pfx.pltime);
> +             if (pfx.expire == 0)
>                       printf(", expire=Never");
> -             else if (p->expire >= now.tv_sec)
> +             else if (pfx.expire >= now.tv_sec)
>                       printf(", expire=%s",
> -                         sec2str(p->expire - now.tv_sec));
> +                         sec2str(pfx.expire - now.tv_sec));
>               else
>                       printf(", expired");
> -             printf(", ref=%d", p->refcnt);
> +             printf(", ref=%d", pfx.refcnt);
>               printf("\n");
>               /*
>                * "advertising router" list is meaningful only if the prefix
>                * information is from RA.
>                */
> -             if (p->advrtrs) {
> +             if (pfx.advrtrs) {
>                       int j;
> -                     struct sockaddr_in6 *sin6;
> +                     struct sockaddr_in6 sin6;
>  
> -                     sin6 = advrtr;
>                       printf("  advertised by\n");
> -                     for (j = 0; j < p->advrtrs; j++) {
> +                     for (j = 0; j < pfx.advrtrs && p <= ep; j++) {
>                               struct in6_nbrinfo *nbi;
>  
> -                             if (getnameinfo((struct sockaddr *)sin6,
> -                                 sin6->sin6_len, namebuf, sizeof(namebuf),
> +                             memcpy(&sin6, p, sizeof(sin6));
> +                             p += sizeof(sin6);
> +
> +                             if (getnameinfo((struct sockaddr *)&sin6,
> +                                 sin6.sin6_len, namebuf, sizeof(namebuf),
>                                   NULL, 0, ninflags) != 0)
>                                       strlcpy(namebuf, "?", sizeof(namebuf));
>                               printf("    %s", namebuf);
>  
> -                             nbi = getnbrinfo(&sin6->sin6_addr,
> -                                 p->if_index, 0);
> +                             nbi = getnbrinfo(&sin6.sin6_addr,
> +                                 pfx.if_index, 0);
>                               if (nbi) {
>                                       switch (nbi->state) {
>                                       case ND6_LLINFO_REACHABLE:
> @@ -1141,7 +1142,6 @@ plist(void)
>                                       }
>                               } else
>                                       printf(" (no neighbor state)\n");
> -                             sin6++;
>                       }
>               } else
>                       printf("  No advertising router\n");
> -- 
> Christian "naddy" Weisgerber                          [email protected]
> 

Reply via email to