Strange bug with #GP(0) after IRET

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
j4cobgarby
Member
Member
Posts: 64
Joined: Fri Jan 26, 2018 11:43 am

Strange bug with #GP(0) after IRET

Post by j4cobgarby »

I thought my interrupt handling system was working, but it seems not. I believe my problem can be summed up as follows:

- My operating system uses long mode in x86-64.
- When IRET is called from an ISR, a #GP exception is raised with error code 0.
- When IRET is called, the top of the stack (in this particular instance) looks like this:

Code: Select all

        Address             Value
STACK 0xffff80007ff7dfa8 = 0xffffffff80000c62
STACK 0xffff80007ff7dfb0 = 0x0000000000000008
STACK 0xffff80007ff7dfb8 = 0x0000000000000286
STACK 0xffff80007ff7dfc0 = 0xffff80007ff7dfd0
STACK 0xffff80007ff7dfc8 = 0x0000000000000010
STACK 0xffff80007ff7dfd0 = 0x0000000000000000
- The GDT looks like this:

Code: Select all

GDT[0x0000]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0008]=Code segment, base=0x00000000, limit=0x00000fff, Execute/Read, Non-Conforming, Accessed, 64-bit
GDT[0x0010]=Data segment, base=0x00000000, limit=0x00000fff, Read/Write, Accessed
- The CPU registers look like this (before IRET):

Code: Select all

rax: 00000000_00000000
rbx: 00000000_00000000
rcx: 00000000_00ffffff
rdx: 00000000_0051fa64
rsp: ffff8000_7ff7dfa8
rbp: ffff8000_7ff7dff0
rsi: 00000000_000000f9
rdi: ffffffff_800030f0
r8 : 00000000_00000010
r9 : 00000000_00000000
r10: 00000000_00000000
r11: 00000000_00000000
r12: 00000000_00000000
r13: 00000000_00000000
r14: 00000000_00000000
r15: 00000000_00000000
rip: ffffffff_80002470
eflags 0x00000282: id vip vif ac vm rf nt IOPL=0 of df IF tf SF zf af pf cf
So, everything looks good. I've verified that 0xffffffff80000c62 (the top of the stack, which should be popped by IRET into RIP) is the correct return address. The CS and SS images on the stack are correct. The RSP image is correct too. I'm unsure about the RFLAGS image on the stack (0x286), but it seems fine.

But when IRET is executed in this state, I get a #GP exception (with error code 0). Furthermore, Bochs gives the following error on the command line:

Code: Select all

10618899027e[CPU0  ] fetch_raw_descriptor: LDTR.valid=0
So, looking through Bochs's source code I can see that this particular message is only shown when the CPU is trying to use the LDT (instead of the GDT as my CS and SS images would suggest). I cannot think of why this would possibly be happening.

But when reasoning about this problem, I thought of something. During IRET, RSP is popped before SS. Perhaps this means that SS would actually be popped from the stack that the RSP image points to? This seems like strange behaviour/CPU design. This can't be the case, right? Presumably the RSP image that's popped is not put into the actual RSP register until the whole IRET frame is popped. But I did just want to verify that.

So according to Intel, possible causes of a #GP(0) exception during IRET (in long mode) are:

If EFLAGS.NT[bit 14] = 1.
If the return code segment selector is NULL.
If the stack segment selector is NULL going back to compatibility mode.
If the stack segment selector is NULL going back to CPL3 64-bit mode.
If a NULL stack segment selector RPL is not equal to CPL going back to non-CPL3 64-bit mode.
If the return instruction pointer is not within the return code segment limit.
If the return instruction pointer is non-canonical.

So. EFLAGS.NT is not set. CS is theoretically not NULL. SS's RPL is theoretically equal to CPL (0). The return instruction pointer is definitely canonical. I'm unsure about the return instruction pointer being within the CS segment limit, but my understanding was under x86-64 long mode, GDT segment's limits and bases were automatically just "the whole of memory", basically?

Can anyone work out why this exception is being raised, then? :)
Octocontrabass
Member
Member
Posts: 5218
Joined: Mon Mar 25, 2013 7:01 pm

Re: Strange bug with #GP(0) after IRET

Post by Octocontrabass »

Which IRET are you using? Some assemblers default to a 32-bit operand size if you don't specify, but you need a 64-bit operand size.
j4cobgarby
Member
Member
Posts: 64
Joined: Fri Jan 26, 2018 11:43 am

Re: Strange bug with #GP(0) after IRET

Post by j4cobgarby »

Good question, I'll check soon when I get back to my computer. I'm using NASM, so I'm not sure what default it uses. But now I think of it, when looking at the disassembly in bochs it did say iret rather than iretq, so that is likely the problem
j4cobgarby
Member
Member
Posts: 64
Joined: Fri Jan 26, 2018 11:43 am

Re: Strange bug with #GP(0) after IRET

Post by j4cobgarby »

Octocontrabass wrote:Which IRET are you using? Some assemblers default to a 32-bit operand size if you don't specify, but you need a 64-bit operand size.
Yes this was in fact the problem :) The amount of hours I spent debugging this for it to come down to adding a single "q" character to my code....
Post Reply