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