Как идентифицировать операции чтения или записи ошибки страницы при использовании обработчика sigaction в SIGSEGV? (LINUX)

Я использую sigaction для обработки исключения ошибки страницы, и функция обработчика определяется следующим образом:

void sigaction_handler(int signum, siginfo_t *info, void *_context)

Таким образом, легко получить адрес ошибки страницы, прочитав info->si_addr.

Вопрос в том, как узнать, является ли эта операция памятью READ или WRITE?

Я обнаружил, что тип параметра _context — это ucontext_t, определенный в /usr/include/sys/ucontext.h

В mcontext_t определено поле cr2, но, к сожалению, оно доступно только тогда, когда x86_64 не определено, поэтому я не мог использовать cr2 для идентификации операций чтения/записи.

С другой стороны, существует структура с именем sigcontext, определенная в /usr/include/bits/sigcontext.h Эта структура содержит поле cr2. Но я не знаю, где его взять.


person user2586415    schedule 16.07.2013    source источник


Ответы (3)


Вы можете проверить это в x86_64, обратившись к структуре mcontext ucontext и регистру ошибок:

void pf_sighandler(int sig, siginfo_t *info, ucontext_t *ctx) {
    ...
    if (ctx->uc_mcontext.gregs[REG_ERR] & 0x2) {
        // Write fault
    } else {
        // Read fault
    }
    ...
}
person webbhorn    schedule 27.04.2014

Вот генерация SIGSEGV из функции ядра arch/x86/mm/fault.c, __bad_area_nosemaphore(): http://lxr.missinglinkelectronics.com/linux+v3.12/arch/x86/mm/fault.c#L760

 760                tsk->thread.cr2         = address;
 761                tsk->thread.error_code  = error_code;
 762                tsk->thread.trap_nr     = X86_TRAP_PF;
 763
 764                force_sig_info_fault(SIGSEGV, si_code, address, tsk, 0);

Есть поле error_code, и его значения также определены в arch/x86/mm/fault.c: http://lxr.missinglinkelectronics.com/linux+v3.12/arch/x86/mm/fault.c#L23

  23/*
  24 * Page fault error code bits:
  25 *
  26 *   bit 0 ==    0: no page found       1: protection fault
  27 *   bit 1 ==    0: read access         1: write access
  28 *   bit 2 ==    0: kernel-mode access  1: user-mode access
  29 *   bit 3 ==                           1: use of reserved bit detected
  30 *   bit 4 ==                           1: fault was an instruction fetch
  31 */
  32enum x86_pf_error_code {
  33
  34        PF_PROT         =               1 << 0,
  35        PF_WRITE        =               1 << 1,
  36        PF_USER         =               1 << 2,
  37        PF_RSVD         =               1 << 3,
  38        PF_INSTR        =               1 << 4,
  39};

Итак, точная информация о типе доступа хранится в thread_struct.error_code: http://lxr.missinglinkelectronics.com/linux+v3.12/arch/x86/include/asm/processor.h#L470

Насколько я вижу, поле error_code не экспортируется в структуру siginfo_t (оно определено в http://man7.org/linux/man-pages/man2/sigaction.2.html .. найдите si_signo).

Так что вы можете

  • Взломать ядро ​​на экспорт tsk->thread.error_code (или проверить, экспортировано уже или нет, например в ptrace)
  • Получить адрес памяти, прочитать /proc/self/maps, разобрать их и проверить биты доступа на странице. Если страница присутствует и доступна только для чтения, единственная возможная ошибка связана с записью, если страница отсутствует, возможны оба вида доступа, а если... не должно быть страниц только для записи.
  • Также можно попробовать найти адрес сбойной инструкции, прочитать ее и разобрать.
person osgx    schedule 19.04.2014
comment
Доступ к информации error_code можно получить через: ((ucontext_t*)context)->uc_mcontext.gregs[REG_ERR]. Он передается аппаратным обеспечением в стеке, который затем передается обработчику сигнала ядром, поскольку ядро ​​передает весь "фрейм". - person Mohammad Alaggan; 26.12.2014
comment
М. Алаган, какое оборудование? - person osgx; 27.12.2014

Доступ к информации error_code можно получить через:

err = ((ucontext_t*)context)->uc_mcontext.gregs[REG_ERR] 

Он передается аппаратным обеспечением в стеке, который затем передается обработчику сигнала ядром, поскольку ядро ​​передает весь "фрейм". потом

bool write_fault = !(err & 0x2);

будет true, если доступ был доступом для записи, и false в противном случае.

person Mohammad Alaggan    schedule 26.12.2014