5

I am trying to understand interrupts in x86 assembly.

I tried to trigger a divide-by-zero error, which corresponds to code 0.

int $0

I was expecting this to have the same behavior as dividing by zero.

movl $0, %edx # dividend
movl $0, %eax # dividend
movl $0, %edi # divisor
divl %edi

In the former case, my program crashes with a "Segmentation fault" and exit code 139 on Linux. In the latter case, my program crashes with a "Floating point exception" and exit code 136 on Linux.

How can I use an interrupt to trigger the same error as calling the div instruction with a zero divisor?

dannyadam
  • 3,950
  • 2
  • 22
  • 19
  • A guess is that the interrupt handler looks at the source of the interrupt and can see that there is no floating point instruction there. – Bo Persson Oct 15 '17 at 15:06
  • @BoPersson: no, the #DE handler doesn't have to disassemble anything. POSIX requires that if integer or FP arithmetic raises a signal, the signal must be SIGFPE. Ironically, in the default environment on most systems, the *only* thing that will raise SIGFPE is integer division, because FP exceptions are masked by default. See https://stackoverflow.com/questions/37262572/on-which-platforms-does-integer-divide-by-zero-trigger-a-floating-point-exceptio. In a SIGFPE handler, you can check for a `FPE_INTDIV_TRAP` code. – Peter Cordes Oct 15 '17 at 23:16

1 Answers1

6

I was expecting this to have the same behavior as dividing by zero.

In principle this is correct.

I tried to trigger a divide-by-zero error, which corresponds to code 0.

int $0

In principle this is still correct.

In the former case, my program crashes with a "Segmentation fault" and exit code 139 on Linux. In the latter case, my program crashes with a "Floating point exception" and exit code 136 on Linux.

x86 CPUs have two operating modes: Real Mode and Protected Mode (64-bit CPUs have a third mode: Long Mode)

In Real Mode you can execute any instruction the CPU is supporting in Real Mode. However Real Mode normally only allows 16-bit code and addressing up to 1 MB of memory. 32-bit operating systems run in Protected Mode.

In Protected Mode two special bits in the cs register indicate if the code currently running belongs to the operating system kernel or to an application.

In Protected Mode ...

  • ... some instructions (e.g. lmsw) can only be called by the operating system
  • ... some instructions (e.g. cli) can only be called by applications if the operating system sets some special bits in some register; otherwise only the operating system can call these instructions
  • ... memory access to some memory regions can be limited to the operating system
  • ... jumping into the operating system code from application code is only allowed to certain addresses ...
  • ... this also applies to interrupts: The operating system can decide which interrupts can be called by applications and which cannot.

If an application tries to do something which is not allowed (e.g. executing some instruction like lmsw or int $0) the CPU will cause an "Segmentation fault" interrupt (because the two bits in cs are indicating that the code does not belong to the operating system). The forbidden instruction will NOT be executed!

If you would call int $0 from a kernel driver (on 32-bit Linux; not on 64-bit Linux) this should have the same effect as a division by zero.

Community
  • 1
  • 1
Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
  • Is it really bits in the `cs` register itself? Not bits in the segment descriptor selected by the value in the `cs` register? – Peter Cordes Oct 15 '17 at 23:13
  • 1
    @Peter, yes, the current privilege level (CPL) is the low two bits of CS. Section 5.2. – prl Oct 16 '17 at 00:21