Как оказалось, всё и тут прозрачно. В доке "Instruction Set Reference", к описанию каждой инструкции приводится её псевдокод (см.раздел Operation). Для SYSRET можно обнаружить следующее, где в явном виде наблюдаем запись в теневые части CS/SS "фиксированных значений" дескрипторов: Спойлер: SYSRET Operation Код (Text): IF (operand size is 64-bit) THEN CS.Selector <-- IA32_STAR[63:48] +10h; ELSE CS.Selector <-- IA32_STAR[63:48]; FI; CS.Selector <-- CS.Selector OR 3; ; RPL forced to 3 SS.Selector <-- IA32_STAR[63:48]+8 OR 3; ; RPL forced to 3 //----------- Set CS to a fixed value -----------// CS.Base <-- 0; ; Flat segment CS.Limit <-- FFFFFh; ; 4-GByte limit CS.Type <-- 1011b; ; Code, RE CS.G <-- 1; ; 4-KByte granularity CS.CPL <-- 3; CS.S <-- 1; CS.P <-- 1; IF (operand size is 64-bit) THEN CS.L <-- 1; ; 64-bit segment CS.D/B <-- 0; ; Required if CS.L = 1 ELSE CS.L <-- 0; ; 32-bit Compatibility mode CS.D/B <-- 1; ; 32-bit segment FI; //----------- Set SS to a fixed value -----------// Copy fixed value CS to SS SS.Type <-- 0011b; ; Data, RW SS.L <-- 0; ; Clear Long-bit SS.D/B <-- 1; ; 32-bit stack segment Если коротко, то здесь говорится.. Если возврат происходит в 64-битный режим, то происходит следующее: 1. В регистр(CS) помещается селектор из IA32_STAR[63:48] +10h. 2. В регистр(SS) помещается селектор из IA32_STAR[63:48] +08h. Если-же возврат в режим совместимости х32, то: 1. В регистр(CS) помещается селектор из IA32_STAR[63:48]. 2. В регистр(SS) помещается селектор из IA32_STAR[63:48] +08h. Как ЦП вычисляет, в какой из режимов происходит возврат? Если размер операндов вызываемой функции равен 64-бита (т.е указан префикс REX.W), то возврат в х64, иначе в х32. Об этом свидетельствует и операция в строке(1) псевдо-кода: IF (operand size is 64-bit). Поскольку в битах IA32_STAR[63:48] лежит значение 0023h, то соответственно получаем селектор(CS) или 23h для х32, или-же: 23h+10h=33h для х64. Значение селектора(SS) в при любых обстоятельствах будет равно: 23h+08h=2Вh. Здесь всплывает ещё нюанс, который актуален для тех, кто пишет свою ОС с расчётом на вызов в ней SYSCALL/SYSRET. Из псевдокода выше видно, что запись значений селекторов в CS/SS жёстко запрограммирована на уровне микро-операций, т.е. прибавляются константы 8 или 10h. От сюда следует, что дескрипторы в таблице GDT должны быть расположены в строго определённом порядке: "Kernel Code/Data", и сразу "User Code/Data". Иначе после SYSRET селекторы будут указывать в космос, а не на валидные дескрипторы в GDT. Если запросить структуру GDT в WinDbg, то оказывается так оно и есть: Код (Text): 0: kd> dg 0 @gdtl P Si Gr Pr Lo Sel Base Limit Type l ze an es ng Flags ---- ----------------- ----------------- ---------- - -- -- -- -- -------- 0000 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 0008 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 0010 00000000`00000000 00000000`00000000 Code RE Ac 0 Nb By P Lo 0000029b 0018 00000000`00000000 00000000`ffffffff Data RW Ac 0 Bg Pg P Nl 00000c93 0020 00000000`00000000 00000000`ffffffff Code RE Ac 3 Bg Pg P Nl 00000cfb 0028 00000000`00000000 00000000`ffffffff Data RW Ac 3 Bg Pg P Nl 00000cf3 0030 00000000`00000000 00000000`00000000 Code RE Ac 3 Nb By P Lo 000002fb 0038 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 0040 00000000`00b92080 00000000`00000067 TSS32 Busy 0 Nb By P Nl 0000008b 0048 00000000`0000ffff 00000000`0000f800 <Reserved> 0 Nb By Np Nl 00000000 0050 ffffffff`fffd9000 00000000`00003c00 Data RW Ac 3 Bg By P Nl 000004f3 0058 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 0060 00000000`00000000 00000000`ffffffff Code RE 0 Bg Pg P Nl 00000c9a 0068 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 0070 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 0078 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 Кстати если не указать WinDbg конкретный селектор, а запросить весь диапазон по размеру таблицы в @gdtl, то он выводит невалидный лог с шагом в 8-байт. Чтобы получить реальные значения селекторов, нужно найти в столбце PL все дескрипторы юзера с PL=3, и прибавить эту тройку к значению их селекторов. Это потому, что младшие 2-бита в селекторе задают привилегию запроса RPL: Если применить к значению любого селектора операцию сдвига SHR,3, в нём останется только индекс дескриптора в GDT, т.е. его порядковый номер. Например из CS юзера "0x23 shr 3" получим номер(4) и т.д. После всех правок, причёсанный лог WinDbg будет выглядеть так (первый столбец с пп для наглядности я добавил сам): Код (Text): 0: kd> dg 0 @gdtl P Si Gr Pr Lo № Sel Base Limit Type l ze an es ng Flags -- ---- ----------------- ----------------- ---------- - -- -- -- -- -------- 00 0000 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 01 0008 00000000`00000000 00000000`00000000 <Reserved> 0 Nb By Np Nl 00000000 02 0010 00000000`00000000 00000000`00000000 Code RE Ac 0 Nb By P Lo 0000029b 03 0018 00000000`00000000 00000000`ffffffff Data RW Ac 0 Bg Pg P Nl 00000c93 04 0023 00000000`00000000 00000000`ffffffff Code RE Ac 3 Bg Pg P Nl 00000cfb ; Исправлено 05 002b 00000000`00000000 00000000`ffffffff Data RW Ac 3 Bg Pg P Nl 00000cf3 ; Исправлено 06 0033 00000000`00000000 00000000`00000000 Code RE Ac 3 Nb By P Lo 000002fb ; Исправлено 08 0040 00000000`00b92080 00000000`00000067 TSS32 Busy 0 Nb By P Nl 0000008b 10 0053 ffffffff`fffd9000 00000000`00003c00 Data RW Ac 3 Bg By P Nl 000004f3 ; Исправлено 12 0060 00000000`00000000 00000000`ffffffff Code RE 0 Bg Pg P Nl 00000c9a Но вернёмся к псевдокоду инструкции SYSRET.. Если на процессорах AMD имеется баг с записью селектора в SS и "фиксированных значений" в теневую его часть, значит этот баг на уровне микроопераций самой инструкции (см.псевдокод). Походу AMD уже исправила ошибку на новых своих процессорах, но что делать обладателям старых ЦП? Как вариант, можно попробовать обновить микрокод процессора. А какая картина в х64 со-входом в кернел через устаревшие SYSENTER/SYSEXIT, и проприетарным int-2Eh? Значения Kernel CS:EIP и SS:ESP для этих инструкций прописаны в MSR IA32_SYSENTER_CS=0x174, IA32_SYSENTER_EIP=0x176, IA32_SYSENTER_ESP=0x175. При этом в кэши дескрипторов CS/SS так-же заносятся фиксированные значения на уровне микрокодов, и к SS прибавляется +8. Когда в MSR IA32-EFER взведёны биты 0 и 8, значения во-всех регистрах IA32_SYSENTER обнуляются. На моём узле в IA32-EFER лежит d01h. Получить из него битовую маску можно метакомандой WinDbg ".formats". Как видим в строке "Binary", все биты взведены, а значит ЦП в режиме Long + Syscall. При этом вызов Sysenter сейчас вызовет исключение доступа Access-Violation (AV): Код (Text): IA32_EFER (0xC0000080) = 00000000`00000d01 бит[0] — RW, SYSCALL enable (SCE), бит[8] — RW, Long mode enable (LME), бит[10] — R, Long mode active (LMA), бит[11] — RW, No Execute Bit enable (NXE). 0: kd> .formats d01 Evaluate expression: Hex: 00000d01 Decimal: 3329 Octal: 0000000000000000006401 Binary: 00000000 00000000 00001101 00000001 Chars: ........ Time: Thu Jan 01 05:55:29 1970 Float: low 4.66492e-042 high 0 Double: 1.64474e-320 Что касается прерывания входа в кернел через INT-2Eh, то его поддержку легко проверить в "таблице диспетчеризации прерываний" IDT. При благоприятных обстоятельствах, в IDT должен лежать указатель на процедуру обслуживания прерывания ISR "Interrupt Service Routine" - смотрим в отладчике: Код (Text): 0: kd> r @idtr, @idtl idtr=fffff800`03fff080 idtl=0fff ;//<--- IDT base & limit 0: kd> !idt Dumping IDT: fffff800`03fff080 00: fffff800`02c75f00 nt!KiDivideErrorFault 01: fffff800`02c76000 nt!KiDebugTrapOrFault 02: fffff800`02c761c0 nt!KiNmiInterrupt Stack = FFFFF800`04011000 03: fffff800`02c76540 nt!KiBreakpointTrap 04: fffff800`02c76640 nt!KiOverflowTrap 05: fffff800`02c76740 nt!KiBoundFault 06: fffff800`02c76840 nt!KiInvalidOpcodeFault 07: fffff800`02c76a80 nt!KiNpxNotAvailableFault 08: fffff800`02c76b40 nt!KiDoubleFaultAbort Stack = FFFFF800`0400F000 09: fffff800`02c76c00 nt!KiNpxSegmentOverrunAbort 0a: fffff800`02c76cc0 nt!KiInvalidTssFault 0b: fffff800`02c76d80 nt!KiSegmentNotPresentFault 0c: fffff800`02c76ec0 nt!KiStackFault 0d: fffff800`02c77000 nt!KiGeneralProtectionFault 0e: fffff800`02c77140 nt!KiPageFault 10: fffff800`02c77500 nt!KiFloatingErrorFault 11: fffff800`02c77680 nt!KiAlignmentFault 12: fffff800`02c77780 nt!KiMcheckAbort Stack = FFFFF800`04013000 13: fffff800`02c77b00 nt!KiXmmException 1f: fffff800`02cc3f10 nt!KiApcInterrupt 2c: fffff800`02c77cc0 nt!KiRaiseAssertion 2d: fffff800`02c77dc0 nt!KiDebugServiceTrap 2f: fffff800`02cc41f0 nt!KiDpcInterrupt ..... Всего в IDT векторов FFh=256, а в данном фрагменте видно, что INT-2Eh в таблице нет, и после 2dh сразу идёт 2fh. Если-же запросить конкретно вектор(2Eh), получим ответ "Unexpected Interrupt", прокол. Командой "u" можно зайти в отладчике по указанному адресу и посмотреть, как ОС обрабатывает подобную ситуацию. Код (Text): 0: kd> !idt 2e Dumping IDT: fffff800`03fff080 2e: fffff800`02db22e0 nt!KxUnexpectedInterrupt0+0x2E0