Subject: The ifconfig(8) unaligned access bug
To: None <port-alpha@NetBSD.ORG>
From: Jason Thorpe <thorpej@nas.nasa.gov>
List: port-alpha
Date: 04/30/1997 18:12:27
So, I looked at the so-called ifconfig bug today, this one:

nostromo /usr/src/sbin/ifconfig 127% obj.alpha/ifconfig lo0 inet 127.0.0.1
pid 12590 (ifconfig): unaligned access: va=0x12013d3fa pc=0x12001bb24 ra=0x12001bb24 op=stl

I checked the problem "appeared" in revision 1.32 of ifconfig.c, when
the appletalk, etc. bits were added.  The actual unaligned access happens
in inet_aton(), when it converts "127.0.0.1" (or any address) to a
struct in_addr.

The problem is that in_getaddr() (in ifconfig.c) is passing inet_aton()
an unaligned pointer... it is aligned to a 16-bit boundary, but not 32.
However, I couldn't find any obvious changes to C code that would cause
this problem.  Indeed, it's passing the address of a struct in_addr
that is contained within a struct sockaddr_in.  "All of these should
be aligned, right?"

in_getaddr() is called with a "which" of 1, which causes:

struct  ifaliasreq      addreq;

the sockaddr in that structure to be used.  So, let's examine for
a moment the declarations at the top of ifconfig.c:

struct  ifreq           ifr, ridreq;
struct  ifaliasreq      addreq;
struct  iso_aliasreq    iso_addreq;

...4 structures in the BSS... now, look closely:

nostromo /usr/src/sbin/ifconfig 128% gdb obj.alpha/ifconfig
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details
GDB 4.16 (alpha-unknown-netbsd1.2C), 
Copyright 1996 Free Software Foundation, Inc...
(gdb) print &ifr
$1 = (struct ifreq *) 0x12013d428
(gdb) print &ridreq
$2 = (struct ifreq *) 0x12013d448
(gdb) print &addreq 
$3 = (struct ifaliasreq *) 0x12013d3e6		<-- Yow!
(gdb) print &iso_addreq 
$4 = (struct iso_aliasreq *) 0x12013d46c

Looking at the namelist:

000000012013d3e0 B at_nr
000000012013d3e6 B addreq
000000012013d428 B ifr
000000012013d448 B ridreq
000000012013d468 B mflag
000000012013d46c B iso_addreq

If you look at struct netrange (<netatalk/at.h>):

struct netrange {
	u_int8_t	nr_phase;
	u_int16_t	nr_firstnet;
	u_int16_t	nr_lastnet;
};

...so, this gets padded to 6 bytes, and addrreq starts at the 6 byte
boundary...

"Gee, that's not right!"

At first I was wondering if this was a linker problem, but then I decided
to look at the compiler output:

        .comm   ifr,32,8
        .comm   ridreq,32,8
        .comm   addreq,64,1		<- YOW!
        .comm   iso_addreq,116,4
        .comm   netmask,16,4
        .comm   at_nr,6,2

...that is most definitely _wrong_.  If I manually frob the alignment to
8, and assemble into ifconfig.o, then link, the binary works fine.

...note that with revision 1.31 of ifconfig.c (before netatalk support),
we have:

        .comm   ifr,32,8
        .comm   ridreq,32,8
        .comm   addreq,64,1		<- SAME LOSSAGE
        .comm   iso_addreq,116,4
        .comm   netmask,16,4

...except there are no 6-byte structures around it, so it Just So Happens
to get aligned correctly.

...now, looking at the compiler sources, I see:

(gcc/config/alpha/alpha.h)

/* Every structure's size must be a multiple of this.  */
#define STRUCTURE_SIZE_BOUNDARY 8

So, my two questions are:

	(1) Why on earth didn't "struct netrange" get rounded to 8
	    bytes, and

	(2) Why on earth doesn't it align "addreq" properly?

...I suppose this is worth a gcc bug report.

Jason R. Thorpe                                       thorpej@nas.nasa.gov
NASA Ames Research Center                               Home: 408.866.1912
NAS: M/S 258-6                                          Work: 415.604.0935
Moffett Field, CA 94035                                Pager: 415.428.6939