Re: Checking of 386bugs

Colin Plumb (colin@nyx.net)
Wed, 30 Jul 97 21:27:55 MDT


The Intel web site reports a slightly different POPAD bug.
http://support.intel.com/oem_developer/embedded_ia/386/general/386SX-B.HTM
says:

80386SX-B STEPPING INFORMATION 12/1/89

2. Problem: The 386 SX CPU inadvertently corrupts the EAX register when
either the POPA or POPAD instruction is followed by an instruction that
uses the EAX register as a base address register AND is using an
additional register as an index register. This sequence of code
appears as:

POPA/POPAD

<instr> REG, <data size> ptr [EAX + <Index Reg>]

The following sample code is an example of the program:

MOV EAX, 4
POPAD
MOV EBX, dword ptr [EAX + EBX*4]

Implications
If the above instruction sequence occurs, the EAX register will contain
an undefined value. Proper operation of the processor cannot be
guaranteed after this sequence is executed until a hardware reset
occurs. This sequence of instructions can occur in the Real, Protected
and Virtual 86 modes of the 386 SX CPU.

Suggested Workaround
Never execute the described instruction sequence. A workaround which
has been proven to be successful in all cases is to insert a NOP
instruction after every POPAD instruction; an example is shown below:

MOV EAX, 4
POPA/POPAD
NOP
MOV EBX, dword ptr [EAX + EBX*4]

Based on that, I think it should be something like:

/* If this fails, it means that any user program may lock CPU hard. Too bad. */
#ifdef CONFIG_M386
__initfunc( static void check_popad(void) )
{
unsigned result, temp;
int x; /* Some random thing to get a pointer to */

printk(KERN_INFO "Checking for POPAD bug...");
__asm__ ("pusha; addl $4,%0; popa; movl 0x55555555(%0,%3,2),%1"
: "=a" (result), "=r" (temp)
: "a" (0x55555555-(unsigned)&x), "r" ((unsigned)&x));
if ((result + (unsigned)&x)) * 3 != -1)
printk("Eek! A malicious user can crash your machine.\n");
else
printk("Ok.\n" );
}
#else
#define check_popad() /*nothing*/
#endif

Note that this version lets GCC worry about getting the right values
into registers, and I used the (%eax,%ebx,2) addressing mode so that
the assembler won't accidentally swap the operands. The value in
eax seems unlikeoy to appear there by accident, and the check for
the right value is written in such a way as to discourage the compiler
from precomputing the right value for comparison.

I also trash %eax before the popa, but not so badly that if the
memory access uses the trashed %eax value, it'll segfault.

-- 
	-Colin