Search code examples
cassemblyx86inlinerdrand

In x86 Assembly, why does DIV throw an exception when the numerator comes from RDRAND?


I've been writing x86 Assembly for years and I've never run across this. I'm hoping someone can point me in the right direction so I can have a "Doh!" moment.

In pseudocode, when I write 42 DIV 4, the division works as expected. When I write RDRAND DIV 4, the CPU throws an exception. I'm doing unsigned, 16-bit arithmetic, but I've reproduced this in 32 and 64-bit as well.

I've reproduced this on Intel Xeon & Core CPUs as well as an AMD EPYC... so I'm thinking this is deliberate and not an isolated thing.

Here's the source code. I'm compiling it like this: clang -masm=intel -o div_bug div_bug.c

#include <stdio.h>
#include <stdint.h>

int main() {
   printf( "div_bug\n" );

   uint8_t rval;

   // rdrand sets CF on success... I've tried this with and without this 
   // check and the program still throws an exception

   // This works as expected
   asm volatile (
//     "try_again_1:"
       "rdrand ax;"           // If CF==0, then the rdrand failed... try again
//     "jnc    try_again_1;"
       "mov    ax, 42;"       // This works... as expected
       "mov    cl, 4;"
       "div    cl;"
       "mov    %0, ah;"
      :"=r" ( rval )          // Output
      :                       // Input
      :"ax", "cl", "cc"   );  // Clobbers

   printf( "The first return value is %u\n", rval );


   // This throws a `Floating point exception (core dumped)`
   asm volatile (
//     "try_again_2:"
       "rdrand ax;"           // If CF==0, then the rdrand failed... try again
//     "jnc    try_again_2;"
//     "mov    ax, 42;"       // <---- Remove this and it breaks
       "mov    cl, 4;"
       "div    cl;"
       "mov    %0, ah;"
      :"=r" ( rval )          // Output
      :                       // Input
      :"ax", "cl", "cc"   );  // Clobbers

   printf( "The second return value is %u\n", rval );

}

I've also got a repo of the code here: https://github.com/marknelsonengineer/div_bug.git

I've researched the DIV and RDRAND instructions in Intel's SDM and I don't see anything obvious.

I'd expect RDRAND to return a number... that I can divide by 4.

My question is: Why does DIV throw an exception when the numerator comes from RDRAND?

Thank you, Mark


Solution

  • Most of the time your random number will be >= 1024, which means that the result of dividing by 4 will not fit in 8 bits, and you'll get a #DE execption. From the Intel manual describing DIV:

    IF OperandSize = 8 (* Word/Byte Operation *)
        THEN
            temp ← AX / SRC;
            IF temp > FFH
                THEN #DE; (* Divide error *)
            ELSE
                AL ← temp;
                AH ← AX MOD SRC;
            FI;
    FI;