[comp.unix.microport] Microport System V/AT 286 C compiler bug

david@bdt.UUCP (David Beckemeyer) (01/11/89)

[ I am mailing this to uport!techs, but they haven't replied to any of
my other mail in the past 6 months, so I don't expect an answer from them.
Besides someone here may know more about this that they do anyways. ]

I believe I have found a bug in the Microport 286 C compiler. I am
running Microport System V/AT 2.4.   This is not a documented bug
that I know of.

The error is that a segment register is mis-handled. In particular the 'ds'
register points to the heap segment when the compiler "thinks" it points to
the data segment. The program is large model. The bug happens when
optimization is off.

I will try to elaborate.  The compiler references the wrong location in
memory on the following instruction:

	udlist[ustat->s_id].u_total += ustat->s_val;

I have included the entire short program at the end that will give the details
of the above structs and variables, but here's the error.

For some reason the compiler screws up the 'ds' register in certain situations
while handling the above expression.  The location of the variables in memory
is very important to make this happen.   In this case, the 'ustat' variable
points to memory acquired with malloc().  The udlist is an array of structs
in the data section.

The assembly code produced by the compiler to deal with the expression is:

0x5700c8 (main:33):  	lds     -8(%bp),%si       [ustat]
0x5700cb (main:33+3):  	imul    $0x6,0x0(%si),%si
0x5700ce (main:33+6):  	mov     $0x67,%ax
0x5700d1 (main:33+9):  	mov     %ax,%ds
0x5700d3 (main:33+0xb):  	lds     -8(%bp),%bx       [ustat]
0x5700d6 (main:33+0xe):  	mov     0x2(%bx),%ax        [0xfef2]
0x5700d9 (main:33+0x11):  	add     %ax,0x20(%si)        [0xfda6]

The problem my not be obvoius at first, but what's happening is that the
ustat structure and the udlist array are in different segments.  The
compiler sets the 'ds' register to the data segment where udlist is (0x67)
and then it uses 'lds' to load the segment for the ustat which wipes out
the value just loaded into 'ds'.  Then it gets the correct value for the
ustat->s_id element (zero in the above case).  The last instruction is
where the bug does its damage.  The compiler fails to reset the 'ds'
segment register to the segment which contains the udlist array, causing
the incorrect effective address to be used.

In the test code example, the udlist array is located at 0x67001c and
the ustat struct is at 0x6f0004.  Because the 'ds' register points to
the segment containing the ustat structure (0x6f), the effective address
of the add instruction is 0x6f0020, instead of 0x670020.  This can either
case a bus trap, or worse, it just might clobber data somewhere else
without you knowing about it.

Here is the short program in its complete form:

---- CUT HERE ----
struct ustat {
	int s_id;
	int s_val;
};

struct ud {
	struct ustat **u_tab;
	int u_total;
};

struct ud udlist[2];

struct ustat *ust[2];

main()
{
	struct ustat **p, *ustat;
	int i;
	char *malloc();

	/* setup to test it */
	ustat = (struct ustat *)malloc(sizeof(struct ustat));
	ustat->s_id = 0;
	ustat->s_val = 8;
	udlist[0].u_total = 0;
	p = udlist[0].u_tab = ust;
	*p = ustat;
	i = 0;

	/* The buggy code */
	p = udlist[i].u_tab;
	ustat = *p;
	udlist[ustat->s_id].u_total += ustat->s_val;
}
------- CUT HERE -------

In case you can't tell, I reduced the above simple case from a "real"
application that I need to get working.  The manipulation of the 'p'
pointer seems significant to the bug, because when it's removed it doesn't
happen (I think).

I would like to know 1) has anybody else experienced this bug, and 2) what
is the work-around for it (and don't say not to use these constructs).
-- 
David Beckemeyer (david@bdt.UUCP)	| "Lester Moore - Four slugs from a .44
Beckemeyer Development Tools		|  no Les, no more."
478 Santa Clara Ave. Oakland, CA 94610	|   - Headstone at Boot Hill
UUCP: {uunet,ucbvax}!unisoft!bdt!david	|     Tombstone, AZ