retf to cpl 2, null ss -> #GP(0) ?

Тема в разделе "WASM.OS.DEVEL", создана пользователем JAPH, 23 июн 2009.

  1. JAPH

    JAPH New Member

    Публикаций:
    0
    Регистрация:
    23 июн 2007
    Сообщения:
    124
    Пытаюсь реализовать вложенный вызов процедур через 64-bit call gate, cpl 3 -> cpl 2 -> cpl 0.

    При первом call far происходит переключение стэка, новый ss = 2.
    Второй call far снова переключает стэк, новый ss = 0, на этом стэке сохраняется селектор 2.
    Далее, retf смотрит, что предыдущий селектор стэка = 2,
    и вот вопрос - does RETF allow SS to be loaded with a NULL selector? Следует ли ожидать возврата управления процедуре на cpl 2 и установки ss = 2?

    Bochs выдает
    и происходит исключение #GP(0). Так и должно быть? В чем подвох?

    Немного кода:
    Код (Text):
    1. .gdt:           dq      0                   ; 0x00 invalid
    2.                 dq      0                   ; 0x08 invalid
    3.                 dq      0x00AF9A000000FFFF          ; 0x10 code
    4.                 dq      0x0040890000000067 + (.tss shl 16)  ; 0x18 TSS
    5.                 dq      0
    6.                 dq      0x00AF92000000FFFF          ; 0x28 data
    7.                 dq      0x00AFDA000000FFFF          ; 0x32 code
    8.                 dq      0x00AFD2000000FFFF          ; 0x3A data
    9.                 dq      0x00AFFA000000FFFF          ; 0x43 code
    10.                 dq      0x00AFF2000000FFFF          ; 0x4B data
    11.                 dq      0x0000EC0000320000 + .ring2     ; 0x52 call gate
    12.                 dq      0
    13.                 dq      0x0000EC0000100000 + .ring0     ; 0x60 call gate
    14.                 dq      0
    15. ; <...>
    16.                 mov     ax, 0x18
    17.                 ltr     ax
    18.                 mov     ax, 0x00 ; 0x28
    19.                 mov     ss, ax
    20.                 mov     ax, 0x4B
    21.                 mov     ds, ax
    22.                 mov     es, ax
    23.                 mov     esp, 0x7C00
    24.                 push    0x4B
    25.                 push    0x60000
    26.                 pushf
    27.                 push    0x43
    28.                 push    .ring3
    29.                 iretq
    30. .cg60:          dq      0
    31.                 dw      0x60
    32. .cg52:          dq      0
    33.                 dw      0x52
    34. .ring3:         call    tbyte [.cg60]
    35.                 call    tbyte [.cg52]
    36.                 hlt                     ; :)
    37. .ring2:         call    tbyte [.cg60]
    38.                 retf                    ; #GP(0) :(
    39. .ring0:         retf
     
  2. JAPH

    JAPH New Member

    Публикаций:
    0
    Регистрация:
    23 июн 2007
    Сообщения:
    124
    Пока копаюсь, выяснилось следующее:

    (код в нулевом кольце)
    Код (Text):
    1. push  0x02 ; и 0x3A пробовалось
    2. push  0x20000
    3. pushf
    4. push  0x32
    5. push  .ring2
    6. iretq
    работает.

    Код (Text):
    1. push  0x3A
    2. push  0x20000
    3. push  0x32
    4. push  .ring2
    5. retf
    работает.

    Код (Text):
    1. push  0x02
    2. push  0x20000
    3. push  0x32
    4. push  .ring2
    5. retf
    выдает #GP(0).
    Так и должно быть?
     
  3. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    JAPH
    В манах посмотреть что мешает ?
    Не знаю как для x64, на x32 нулевой селектор генерирует экцепшин общей защиты, там черным по белому кучу раз написано.
     
  4. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    Вообще, странно. Как я понимаю, продвижение r3 ->... ->r0 возможно только с помощью far jmp/call, а назад, r0 ->... ->r3 только с помощью retf/iret. А тут ситуация немного наоброт получается?
     
  5. JAPH

    JAPH New Member

    Публикаций:
    0
    Регистрация:
    23 июн 2007
    Сообщения:
    124
    Mika0x65
    Тут изначально предполагалось перемещение: r3 -> r2 -> r0 через call gate, r0 -> r2 -> r3 через retf. На первом retf все валится.
    Вопрос свелся к #2 - каково поведение retf, если сохраненный селектор стэка - 2? Это #GP(0) или нормальный возврат? В случае iretq нету никаких исключений.

    PS. Был бы 64-битный процессор, проверил бы) ан нету)

    Clerk
    В 32-битном режиме согласен - #GP(0) без вопросов.
    В 64-битном - приведена цитата из мануала, где на самом интересном месте IRET.
     
  6. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    JAPH
    Я никогда x64 не интересовался, там всё криво сделано, поэтому чтото утвержать не могу, там может быть что угодно. Следует посмотреть на псевдокод обработки межкольцевого call.
     
  7. JAPH

    JAPH New Member

    Публикаций:
    0
    Регистрация:
    23 июн 2007
    Сообщения:
    124
    Clerk
    В псевдокоде call все в порядке. А вот в retf начинаются недомолвки.
    В IA-32E-MODE-RETURN-OUTER-PRIVILEGE-LEVEL среди прочих есть проверочка
    В iret она тоже есть. Вопрос в том: всегда должна выполняться эта проверка или только, когда селектор ненулевой?

    Смотрим на исходный код bochs 2.4.1.
    cpu/iret.cc
    Код (Text):
    1.   void BX_CPP_AttrRegparmN(1)
    2. BX_CPU_C::long_iret(bxInstruction_c *i)
    3. {
    4. <...>
    5. /* INTERRUPT RETURN TO OUTER PRIVILEGE LEVEL or 64 BIT MODE */
    6. <...>
    7.     if ((raw_ss_selector & 0xfffc) == 0) {
    8.       if (! IS_LONG64_SEGMENT(cs_descriptor) || cs_selector.rpl == 3) {
    9.         BX_ERROR(("iret64: SS selector null"));
    10.         exception(BX_GP_EXCEPTION, 0, 0);
    11.       }
    12.     }
    13.     else {
    14. <...>
    15.       /* AR byte must indicate a writable data segment,
    16.        * else #GP(SS selector) */
    17.       if (ss_descriptor.valid==0 || ss_descriptor.segment==0 ||
    18.           IS_CODE_SEGMENT(ss_descriptor.type) ||
    19.          !IS_DATA_SEGMENT_WRITEABLE(ss_descriptor.type))
    20.       {
    21.         BX_ERROR(("iret64: SS AR byte not writable or code segment"));
    22.         exception(BX_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0);
    23.       }
    24. <...>
    25.     }
    26. <...>
    27. }
    Видно, что проверка на writable выполняется только когда селектор ненулевой.
    В cpu/ret_far.cc проверка на writable выполняется и когда селектор нулевой. Эта проверка неудачна, и
    Код (Text):
    1.       BX_ERROR(("return_protected: SS.AR byte not writable data"));
     
  8. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    А что в других эмуляторах происходит? Например, в QEMU?
     
  9. JAPH

    JAPH New Member

    Публикаций:
    0
    Регистрация:
    23 июн 2007
    Сообщения:
    124
    В qemu инструкция retf работает без исключений, даже в случае сохраненного селектора стэка = 2. То есть
    Код (Text):
    1. push  0x02
    2. push  0x20000
    3. push  0x32
    4. push  .ring2
    5. retf
    передает управление во второе кольцо.

    Однако в qemu вызов call far m16:64
    Код (Text):
    1. call tbyte [.cg60]
    производит #GP(0), а вызов call far m16:32
    Код (Text):
    1. call fword [.cg60+4]
    генерирует #TS(0). Bochs же проходит оба этих вызова без исключений.
     
  10. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    а так же INT,SYSENTER - для повышения привилегий, и SYSEXIT для r0->r3
     
  11. JAPH

    JAPH New Member

    Публикаций:
    0
    Регистрация:
    23 июн 2007
    Сообщения:
    124
    Кстати, вообще странно, как вычислилось #TS(0).
    Потому что сначала обработчика этого исключения не было - нуль в IDT. По идее, если происходит #TS(0) и обработчика нету - в процессе вызова обработчика исключения случилось исключение #GP(0xA2), оба исключения класса Contributory => это уже #DF, а так как и обработчика #DF нету, то triple fault. Однако qemu сгенерировала #GP(0xA2).

    Bochs на ее месте вполне корректно перезагрузилась.