iret
Interrupt Return
Returns from an interrupt, exception, or task handler.
Details
Returns from an interrupt handler, exception handler, or nested task by popping the return address and EFLAGS from the stack and resuming execution at the interrupted instruction. In protected mode, also restores CPL and task context; in 64-bit mode (IRETQ), pops a 64-bit return address. This instruction performs memory serialization and may reload segment registers, making it a heavy-weight operation that flushes pending stores.
Pseudocode Operation
if OperandSize = 16:
temp_IP ← [SP]; SP ← SP + 2
temp_CS ← [SP]; SP ← SP + 2
EFLAGS ← [SP]; SP ← SP + 2
else if OperandSize = 32:
temp_EIP ← [ESP]; ESP ← ESP + 4
temp_CS ← [ESP]; ESP ← ESP + 4
EFLAGS ← [ESP]; ESP ← ESP + 4
else if OperandSize = 64:
temp_RIP ← [RSP]; RSP ← RSP + 8
temp_CS ← [RSP]; RSP ← RSP + 8
EFLAGS ← [RSP]; RSP ← RSP + 8
CS ← temp_CS
IP/EIP/RIP ← temp_IP/EIP/RIP
Example
Encoding
Operands
Reference (Intel® SDM)
Instruction Forms
| Opcode | Instruction | Op/En | 64/32-bit Mode | CPUID | Description |
|---|---|---|---|---|---|
| CF | IRET | ZO | Valid Valid | Interrupt return (16-bit operand size). | |
| CF | IRETD | ZO | Valid Valid | Interrupt return (32-bit operand size). | |
| REX.W + CF | IRETQ | ZO | Valid N.E. | Interrupt return (64-bit operand size). |
Description
Operation
IF PE = 0 THEN GOTO REAL-ADDRESS-MODE; ELSIF (IA32_EFER.LMA = 0) THEN IF (EFLAGS.VM = 1) THEN GOTO RETURN-FROM-VIRTUAL-8086-MODE; ELSE GOTO PROTECTED-MODE; FI; ELSE GOTO IA-32e-MODE; FI; REAL-ADDRESS-MODE; IF OperandSize = 32 THEN EIP := Pop(); CS := Pop(); (* 32-bit pop, high-order 16 bits discarded *) tempEFLAGS := Pop(); EFLAGS := (tempEFLAGS AND 257FD5H) OR (EFLAGS AND 1A0000H); ELSE (* OperandSize = 16 *) EIP := Pop(); (* 16-bit pop; clear upper 16 bits *) CS := Pop(); (* 16-bit pop *) EFLAGS[15:0] := Pop(); FI; END; RETURN-FROM-VIRTUAL-8086-MODE: (* Processor is in virtual-8086 mode when IRET is executed and stays in virtual-8086 mode *) IF IOPL = 3 (* Virtual mode: PE = 1, VM = 1, IOPL = 3 *) THEN IF OperandSize = 32 THEN EIP := Pop(); CS := Pop(); (* 32-bit pop, high-order 16 bits discarded *) EFLAGS := Pop(); (* VM, IOPL,VIP and VIF EFLAG bits not modified by pop *) IF EIP not within CS limit THEN #GP(0); FI; ELSE (* OperandSize = 16 *) EIP := Pop(); (* 16-bit pop; clear upper 16 bits *) IRET/IRETD/IRETQ—Interrupt Return Vol. 2A 3-491 CS := Pop(); (* 16-bit pop *) EFLAGS[15:0] := Pop(); (* IOPL in EFLAGS not modified by pop *) IF EIP not within CS limit THEN #GP(0); FI; FI; ELSE #GP(0); (* Trap to virtual-8086 monitor: PE = 1, VM = 1, IOPL < 3 *) FI; END; PROTECTED-MODE: IF NT = 1 THEN GOTO TASK-RETURN; (* PE = 1, VM = 0, NT = 1 *) FI; IF OperandSize = 32 THEN EIP := Pop(); CS := Pop(); (* 32-bit pop, high-order 16 bits discarded *) tempEFLAGS := Pop(); ELSE (* OperandSize = 16 *) EIP := Pop(); (* 16-bit pop; clear upper bits *) CS := Pop(); (* 16-bit pop *) tempEFLAGS := Pop(); (* 16-bit pop; clear upper bits *) FI; IF tempEFLAGS(VM) = 1 and CPL = 0 THEN GOTO RETURN-TO-VIRTUAL-8086-MODE; ELSE GOTO PROTECTED-MODE-RETURN; FI; TASK-RETURN: (* PE = 1, VM = 0, NT = 1 *) SWITCH-TASKS (without nesting) to TSS specified in link field of current TSS; Mark the task just abandoned as NOT BUSY; IF EIP is not within CS limit THEN #GP(0); FI; END; RETURN-TO-VIRTUAL-8086-MODE: (* Interrupted procedure was in virtual-8086 mode: PE = 1, CPL=0, VM = 1 in flag image *) (* If shadow stack or indirect branch tracking at CPL3 then #GP(0) *) IF CR4.CET AND (IA32_U_CET.ENDBR_EN OR IA32_U_CET.SHSTK_EN) THEN #GP(0); FI; shadowStackEnabled = ShadowStackEnabled(CPL) IF EIP not within CS limit THEN #GP(0); FI; EFLAGS := tempEFLAGS; ESP := Pop(); SS := Pop(); (* Pop 2 words; throw away high-order word *) ES := Pop(); (* Pop 2 words; throw away high-order word *) DS := Pop(); (* Pop 2 words; throw away high-order word *) FS := Pop(); (* Pop 2 words; throw away high-order word *) GS := Pop(); (* Pop 2 words; throw away high-order word *) IF shadowStackEnabled (* check if 8 byte aligned *) IF SSP AND 0x7 != 0 IRET/IRETD/IRETQ—Interrupt Return Vol. 2A 3-492 THEN #CP(FAR-RET/IRET); FI; FI; CPL := 3; (* Resume execution in Virtual-8086 mode *) tempOldSSP = SSP; (* Now past all faulting points; safe to free the token. The token free is done using the old SSP * and using a supervisor override as old CPL was a supervisor privilege level *) IF shadowStackEnabled expected_token_value = tempOldSSP | BUSY_BIT (* busy bit - bit position 0 - must be set *) new_token_value = tempOldSSP (* clear the busy bit *) shadow_stack_lock_cmpxchg8b(tempOldSSP, new_token_value, expected_token_value) FI; END; PROTECTED-MODE-RETURN: (* PE = 1 *) IF CS(RPL) > CPL THEN GOTO RETURN-TO-OUTER-PRIVILEGE-LEVEL; ELSE GOTO RETURN-TO-SAME-PRIVILEGE-LEVEL; FI; END; RETURN-TO-OUTER-PRIVILEGE-LEVEL: IF OperandSize = 32 THEN tempESP := Pop(); tempSS := Pop(); (* 32-bit pop, high-order 16 bits discarded *) ELSE IF OperandSize = 16 THEN tempESP := Pop(); (* 16-bit pop; clear upper bits *) tempSS := Pop(); (* 16-bit pop *) ELSE (* OperandSize = 64 *) tempRSP := Pop(); tempSS := Pop(); (* 64-bit pop, high-order 48 bits discarded *) FI; IF new mode ≠ 64-Bit Mode THEN IF EIP is not within CS limit THEN #GP(0); FI; ELSE (* new mode = 64-bit mode *) IF RIP is non-canonical THEN #GP(0); FI; FI; EFLAGS (CF, PF, AF, ZF, SF, TF, DF, OF, NT) := tempEFLAGS; IF OperandSize = 32 or OperandSize = 64 THEN EFLAGS(RF, AC, ID) := tempEFLAGS; FI; IF CPL ≤ IOPL THEN EFLAGS(IF) := tempEFLAGS; FI; IF CPL = 0 THEN EFLAGS(IOPL) := tempEFLAGS; IF OperandSize = 32 or OperandSize = 64 THEN EFLAGS(VIF, VIP) := tempEFLAGS; FI; FI; IF ShadowStackEnabled(CPL) IRET/IRETD/IRETQ—Interrupt Return Vol. 2A 3-493 (* check if 8 byte aligned *) IF SSP AND 0x7 != 0 THEN #CP(FAR-RET/IRET); FI; IF CS(RPL) != 3 THEN tempSsCS = shadow_stack_load 8 bytes from SSP+16; tempSsLIP = shadow_stack_load 8 bytes from SSP+8; tempSSP = shadow_stack_load 8 bytes from SSP; SSP = SSP + 24; (* Do 64 bit compare to detect bits beyond 15 being set *) tempCS = CS; (* zero padded to 64 bit *) IF tempCS != tempSsCS THEN #CP(FAR-RET/IRET); FI; (* Do 64 bit compare; pad CSBASE+RIP with 0 for 32 bit LIP *) IF CSBASE + RIP != tempSsEIP THEN #CP(FAR-RET/IRET); FI; (* check if 4 byte aligned *) IF tempSSP AND 0x3 != 0 THEN #CP(FAR-RET/IRET); FI; FI; FI; tempOldCPL = CPL; CPL := CS(RPL); IF OperandSize = 64 THEN RSP := tempRSP; SS := tempSS; ELSE ESP := tempESP; SS := tempSS; FI; IF new mode != 64-Bit Mode THEN IF EIP is not within CS limit THEN #GP(0); FI; ELSE (* new mode = 64-bit mode *) IF RIP is non-canonical THEN #GP(0); FI; FI; tempOldSSP = SSP; IF ShadowStackEnabled(CPL) IF CPL = 3 THEN tempSSP := IA32_PL3_SSP; FI; IF ((IA32_EFER.LMA AND CS.L) = 0 AND tempSSP[63:32] != 0) OR ((IA32_EFER.LMA AND CS.L) = 1 AND tempSSP is not canonical relative to the current paging mode) THEN #GP(0); FI; SSP := tempSSP FI; (* Now past all faulting points; safe to free the token. The token free is done using the old SSP * and using a supervisor override as old CPL was a supervisor privilege level *) IF ShadowStackEnabled(tempOldCPL) expected_token_value = tempOldSSP | BUSY_BIT (* busy bit - bit position 0 - must be set *) new_token_value = tempOldSSP (* clear the busy bit *) shadow_stack_lock_cmpxchg8b(tempOldSSP, new_token_value, expected_token_value) IRET/IRETD/IRETQ—Interrupt Return Vol. 2A 3-494 FI; FOR each SegReg in (ES, FS, GS, and DS) DO tempDesc := descriptor cache for SegReg (* hidden part of segment register *) IF (SegmentSelector == NULL) OR (tempDesc(DPL) < CPL AND tempDesc(Type) is (data or non-conforming code))) THEN (* Segment register invalid *) SegmentSelector := 0; (*Segment selector becomes null*) FI; OD; END; RETURN-TO-SAME-PRIVILEGE-LEVEL: (* PE = 1, RPL = CPL *) IF new mode ≠ 64-Bit Mode THEN IF EIP is not within CS limit THEN #GP(0); FI; ELSE (* new mode = 64-bit mode *) IF RIP is non-canonical THEN #GP(0); FI; FI; EFLAGS (CF, PF, AF, ZF, SF, TF, DF, OF, NT) := tempEFLAGS; IF OperandSize = 32 or OperandSize = 64 THEN EFLAGS(RF, AC, ID) := tempEFLAGS; FI; IF CPL ≤ IOPL THEN EFLAGS(IF) := tempEFLAGS; FI; IF CPL = 0 THEN EFLAGS(IOPL) := tempEFLAGS; IF OperandSize = 32 or OperandSize = 64 THEN EFLAGS(VIF, VIP) := tempEFLAGS; FI; FI; IF ShadowStackEnabled(CPL) IF SSP AND 0x7 != 0 (* check if aligned to 8 bytes *) THEN #CP(FAR-RET/IRET); FI; tempSsCS = shadow_stack_load 8 bytes from SSP+16; tempSsLIP = shadow_stack_load 8 bytes from SSP+8; tempSSP = shadow_stack_load 8 bytes from SSP; SSP = SSP + 24; tempCS = CS; (* zero padded to 64 bit *) IF tempCS != tempSsCS (* 64 bit compare; CS zero padded to 64 bits *) THEN #CP(FAR-RET/IRET); FI; IF CSBASE + RIP != tempSsLIP (* 64 bit compare; CSBASE+RIP zero padded to 64 bit for 32 bit LIP *) THEN #CP(FAR-RET/IRET); FI; IF tempSSP AND 0x3 != 0 (* check if aligned to 4 bytes *) THEN #CP(FAR-RET/IRET); FI; IF ((IA32_EFER.LMA AND CS.L) = 0 AND tempSSP[63:32] != 0) OR ((IA32_EFER.LMA AND CS.L) = 1 AND tempSSP is not canonical relative to the current paging mode) THEN #GP(0); FI; FI; IF ShadowStackEnabled(CPL) IF IA32_EFER.LMA = 1 (* In IA-32e-mode the IRET may be switching stacks if the interrupt/exception was delivered through an IDT with a non-zero IST *) IRET/IRETD/IRETQ—Interrupt Return Vol. 2A 3-495 (* In IA-32e mode for same CPL IRET there is always a stack switch. The below check verifies if the stack switch was to self stack and if so, do not try to free the token on this shadow stack. If the tempSSP was not to same stack then there was a stack switch so do attempt to free the token *) IF tempSSP != SSP THEN expected_token_value = SSP | BUSY_BIT (* busy bit - bit position 0 - must be set *) new_token_value = SSP (* clear the busy bit *) shadow_stack_lock_cmpxchg8b(SSP, new_token_value, expected_token_value) FI; FI; SSP := tempSSP FI; END; IA-32e-MODE: IF NT = 1 THEN #GP(0); ELSE IF OperandSize = 32 THEN EIP := Pop(); CS := Pop(); tempEFLAGS := Pop(); ELSE IF OperandSize = 16 THEN EIP := Pop(); (* 16-bit pop; clear upper bits *) CS := Pop(); (* 16-bit pop *) tempEFLAGS := Pop(); (* 16-bit pop; clear upper bits *) FI; ELSE (* OperandSize = 64 *) THEN RIP := Pop(); CS := Pop(); (* 64-bit pop, high-order 48 bits discarded *) tempRFLAGS := Pop(); FI; IF CS.RPL < CPL or (CR4.FRED = 1 and CS.RPL > CPL) THEN #GP(CS.selector); FI; IF CS.RPL > CPL THEN GOTO RETURN-TO-OUTER-PRIVILEGE-LEVEL; ELSE (* CS.RPL = CPL *) IF CR4.FRED = 1 and CPL = 0 and CS.L = 0 THEN #GP(CS.selector); FI; IF instruction began in 64-Bit Mode THEN IF OperandSize = 32 THEN ESP := Pop(); SS := Pop(); (* 32-bit pop, high-order 16 bits discarded *) ELSE IF OperandSize = 16 THEN ESP := Pop(); (* 16-bit pop; clear upper bits *) SS := Pop(); (* 16-bit pop *) ELSE (* OperandSize = 64 *) RSP := Pop(); SS := Pop(); (* 64-bit pop, high-order 48 bits discarded *) IRET/IRETD/IRETQ—Interrupt Return Vol. 2A 3-496 FI; FI; GOTO RETURN-TO-SAME-PRIVILEGE-LEVEL; FI; END;
Flags Affected
All the flags and fields in the EFLAGS register are potentially modified, depending on the mode of operation of the processor. If performing a return from a nested task to a previous task, the EFLAGS register will be modified according to the EFLAGS image stored in the previous task’s TSS.