2013-07-16 12 views
5

Io uso sigaction per gestire un'eccezione errore di pagina, e la funzione del gestore è defind come questo:Come identificare leggere o scrivere operazioni di errore di pagina quando si utilizza gestore sigaction su SIGSEGV (LINUX)

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

Quindi è facile ottenere l'indirizzo di errore di pagina leggendo info-> si_addr.

La domanda è: come sapere se questa operazione è la memoria LEGGERE o WRITE?

ho trovato il tipo di _context parametro è ucontext_t definito in/usr/include/sys/ucontext.h

C'è un campo CR2 definito mcontext_t, ma unforunately, è solo avaliable quando x86_64 non è defind, quindi non ho potuto usare cr2 per identificare operazioni di lettura/scrittura.

In anotherway, v'è una struttura denominata sigcontext definito in/usr/include/bits/sigcontext.h Questo struct contiene CR2 campo. Ma non so dove trovarlo.

risposta

6

Ecco la generazione di SIGSEGV dal kernel arch/x86/mm/fault.c, __bad_area_nosemaphore() funzione: 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); 

C'è error_code campo, ed i valori sono definiti in arch/x86/mm/fault.c troppo: 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}; 

Pertanto, le informazioni esatte sul tipo di accesso sono memorizzate nello thread_struct.error_code: http://lxr.missinglinkelectronics.com/linux+v3.12/arch/x86/include/asm/processor.h#L470

Il campo error_code non viene esportata in siginfo_t struct come la vedo io (che è definito in http://man7.org/linux/man-pages/man2/sigaction.2.html .. cercare si_signo).

in modo da poter

  • Hack al kernel di esportare tsk->thread.error_code (o controllare, è esso esportato già o no, per esempio in ptrace)
  • Indirizzo di memoria, leggere /proc/self/maps, li analizzare e controllo accesso ai bit sulla pagina. Se la pagina è presente e di sola lettura, l'unico errore possibile è la scrittura, se la pagina non è presente sono possibili entrambi i tipi di accesso e se ... non dovrebbero esserci pagine di sola scrittura.
  • Inoltre, è possibile provare a trovare l'indirizzo dell'istruzione non riuscita, leggerlo e smontarlo.
+2

Le informazioni error_code sono accessibili tramite: (contesto (ucontext_t *)) -> uc_mcontext.gregs [REG_ERR]. Viene passato dall'hardware sullo stack, che viene poi passato al gestore del segnale dal kernel, poiché il kernel passa l'intero 'frame '. –

+0

M. Alaggan, quale hardware? – osgx

+0

L'ho provato su x86-64. –

3

È possibile controllare questo in x86_64 facendo riferimento alla struct mcontext del ucontext e il registro err:

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

Le informazioni error_code si può accedere attraverso:

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

Si passa da l'hardware sullo stack, che viene poi passato al gestore del segnale dal kernel, poiché il kernel passa l'intero `frame '. Quindi

bool write_fault = !(err & 0x2); 

sarà vero se l'accesso è stato un accesso in scrittura e falso in caso contrario.

Problemi correlati