Domanda

Quindi, so che Linux utilizza quattro segmenti predefiniti per un processore x86 (codice del kernel, dati del kernel, codice utente, dati utente), ma tutti hanno la stessa base e il limite (0x00000000 e 0xFFFFF), significa che ogni mappe segmento allo stesso insieme di indirizzi lineari.

Dato questo, perché nemmeno segmenti utente / kernel? Capisco il motivo per cui ci dovrebbe essere segmenti separati per il codice e dati (solo a causa di come le offerte dei processori x86 con i registri CS e DS), ma perché non hanno un segmento di codice singolo e un singolo segmento di dati? Protezione della memoria avviene attraverso paging, ei segmenti di utenza e del kernel map agli stessi indirizzi lineari in ogni caso.

È stato utile?

Soluzione

L'architettura x86 associa un tipo e un livello di privilegio con ogni descrittore segmento. Il tipo di un descrittore permette segmenti da apportare sola lettura, lettura / scrittura, eseguibili, ecc, ma il motivo principale per i diversi segmenti aventi la medesima base e il limite è di consentire un diverso livello di privilegio descrittore (DPL) da utilizzare.

Il DPL è due bit, consentendo i valori da 0 a 3 da codificare. Quando il livello di privilegio è 0, allora si dice che sia anello 0 , che è la più privilegiata. I descrittori di segmento per il kernel di Linux sono l'anello 0, mentre i descrittori di segmento per lo spazio utente sono ring 3 (almeno privilegiato). Questo è vero per la maggior parte dei sistemi operativi segmentati; il nucleo del sistema operativo è anello 0 e il resto è anello 3.

set di The Linux Kernel fino, come lei ha ricordato, quattro segmenti:

  • __ KERNEL_CS (segmento di codice del kernel, base = 0, = limite 4GB, tipo = 10, DPL = 0)
  • __ KERNEL_DS (segmento di dati Kernel, base = 0, = limite 4GB, tipo = 2, DPL = 0)
  • __ USER_CS (segmento di codice utente, base = 0, il limite = 4 GB, type = 10, DPL = 3)
  • __ user_ds (segmento di dati utente, base = 0, il limite = 4 GB, type = 2, DPL = 3)

La base e il limite di tutti e quattro sono le stesse, ma i segmenti kernel sono DPL 0, i segmenti utente sono DPL 3, i segmenti di codice sono eseguibili e leggibile (non scrivibile), ed i segmenti di dati sono leggibili e scrivibili ( non eseguibile).

Vedi anche:

Altri suggerimenti

L'architettura di gestione della memoria x86 utilizza sia segmentazione e paginazione. Molto grosso modo, un segmento è una partizione di spazio di indirizzamento di un processo che ha una propria politica di protezione. Così, nell'architettura x86, è possibile suddividere l'intervallo di indirizzi di memoria che un processo vede in più segmenti contigui, e assegnare diverse modalità di protezione a ciascuno. Paging è una tecnica per la mappatura di piccole dimensioni (di solito 4KB) regioni di spazio indirizzo di un processo per pezzi di vera e propria, la memoria fisica. Paging controlla quindi come limitata all'interno di un segmento vengono mappati sui RAM fisica.

Tutti i processi hanno due segmenti:

  1. un segmento (indirizzi 0x00000000 attraverso 0xBFFFFFFF) per i dati a livello utente, processi specifici come codice del programma, dati statici, mucchio, e pila. Ogni processo ha la sua, segmento di utenti indipendenti.

  2. Un segmento (indirizzi 0xC0000000 attraverso 0xFFFFFFFF), che contiene i dati kernel-specifici come ad esempio le istruzioni del kernel, dati, alcune pile su cui il codice del kernel in grado di eseguire, e più interessante, una regione in questo segmento è direttamente mappati alla memoria fisica, in modo che il kernel può accedere direttamente a locazioni di memoria fisiche, senza doversi preoccupare di traduzione degli indirizzi. Lo stesso segmento kernel è mappato in ogni processo, ma i processi può accedere solo durante l'esecuzione in modalità kernel protetta.

Quindi, in modalità utente, il processo può solo indirizzi di accesso meno 0xC0000000; qualsiasi accesso a un indirizzo superiore a questo si traduce in un errore. Tuttavia, quando un processo in modalità utente inizia ad eseguire nel kernel (per esempio, dopo aver effettuato una chiamata di sistema), il bit di protezione nella CPU passa alla modalità supervisore (e alcuni registri segmentazione sono cambiati), significa che il processo è quindi in grado di accedere agli indirizzi sopra 0xC0000000.

vedi Ed da: qui

in X86 - registri di segmento Linux sono utilizzati per il controllo del buffer [vedere il frammento di codice di seguito, definiti alcuni array char in pila]:

static void
printint(int xx, int base, int sgn)
{
    char digits[] = "0123456789ABCDEF";
    char buf[16];
    int i, neg;
    uint x;

    neg = 0;
    if(sgn && xx < 0){
        neg = 1;
        x = -xx;
    } else {
        x = xx;
    }

    i = 0;
    do{
        buf[i++] = digits[x % base];
    }while((x /= base) != 0);
    if(neg)
        buf[i++] = '-';

    while(--i >= 0)
        my_putc(buf[i]);
}

Ora, se vediamo i dis-assemblaggio del codice gcc-codice generato.

Dump di codice assembler per la funzione PRINTINT:

 0x00000000004005a6 <+0>:   push   %rbp
   0x00000000004005a7 <+1>: mov    %rsp,%rbp
   0x00000000004005aa <+4>: sub    $0x50,%rsp
   0x00000000004005ae <+8>: mov    %edi,-0x44(%rbp)


  0x00000000004005b1 <+11>: mov    %esi,-0x48(%rbp)
   0x00000000004005b4 <+14>:    mov    %edx,-0x4c(%rbp)
   0x00000000004005b7 <+17>:    mov    %fs:0x28,%rax  ------> obtaining an 8 byte guard from based on a fixed offset from fs segment register [from the descriptor base in the corresponding gdt entry]
   0x00000000004005c0 <+26>:    mov    %rax,-0x8(%rbp) -----> pushing it as the first local variable on to stack
   0x00000000004005c4 <+30>:    xor    %eax,%eax
   0x00000000004005c6 <+32>:    movl   $0x33323130,-0x20(%rbp)
   0x00000000004005cd <+39>:    movl   $0x37363534,-0x1c(%rbp)
   0x00000000004005d4 <+46>:    movl   $0x42413938,-0x18(%rbp)
   0x00000000004005db <+53>:    movl   $0x46454443,-0x14(%rbp)

...
...
  // function end

   0x0000000000400686 <+224>:   jns    0x40066a <printint+196>
   0x0000000000400688 <+226>:   mov    -0x8(%rbp),%rax -------> verifying if the stack was smashed
   0x000000000040068c <+230>:   xor    %fs:0x28,%rax  --> checking the value on stack is matching the original one based on fs
   0x0000000000400695 <+239>:   je     0x40069c <printint+246>
   0x0000000000400697 <+241>:   callq  0x400460 <__stack_chk_fail@plt>
   0x000000000040069c <+246>:   leaveq 
   0x000000000040069d <+247>:   retq 

Ora, se togliamo gli array char pila base da questa funzione, gcc non genererà questo controllo guardia.

ho visto lo stesso generato da gcc anche per i moduli del kernel. Fondamentalmente io stavo vedendo un incidente mentre botrapping po 'di codice del kernel ed è stato provocato l'errore con l'indirizzo 0x28 virtuale. Più tardi ho pensato che pensavo di aver inizializzato lo stack pointer in modo corretto e caricato correttamente il programma, io non sto avendo le voci giuste GDT, che si tradurrebbe i fs based di offset in un indirizzo virtuale valido.

Tuttavia, in caso di codice del kernel è stato semplicemente ignorato, l'errore invece di saltare a qualcosa come __stack_chk_fail @ plt>.

L'opzione del compilatore pertinenti che aggiunge questa guardia in gcc è -fstack-protettore. Penso che questa è abilitata di default, che la compilazione di un'applicazione utente.

Per i kernel, si può abilitare questo flag gcc opzione di configurazione CC_STACKPROTECTOR via.

config CC_STACKPROTECTOR
 699        bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
 700        depends on SUPERH32
 701        help
 702          This option turns on the -fstack-protector GCC feature. This
 703          feature puts, at the beginning of functions, a canary value on
 704          the stack just before the return address, and validates
 705          the value just before actually returning.  Stack based buffer
 706          overflows (that need to overwrite this return address) now also
 707          overwrite the canary, which gets detected and the attack is then
 708          neutralized via a kernel panic.
 709
 710          This feature requires gcc version 4.2 or above.

Il file del kernel pertinenti laddove ciò gs / FS è linux / arch / x86 / include / asm / stackprotector.h

di memoria del kernel non dovrebbe essere leggibile da programmi in esecuzione in spazio utente.

I dati del programma spesso non è eseguibile (DEP, una caratteristica del processore, che aiuta a guardia contro l'esecuzione di un buffer overflow e altri attacchi dannosi).

E 'tutta una questione di controllo di accesso - diversi segmenti hanno diritti diversi. Questo è il motivo di accesso a segmento sbagliato vi darà una "segmentation fault".

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top