Autorizzazione exec imprevista da mmap quando i file di assembly sono inclusi nel progetto


94

Sto sbattendo la testa contro il muro con questo.

Nel mio progetto, quando sto allocando memoria con mmapil mapping ( /proc/self/maps) mostra che è una regione leggibile ed eseguibile nonostante abbia richiesto solo memoria leggibile.

Dopo aver esaminato strace (che sembrava buono) e altri debug, sono stato in grado di identificare l'unica cosa che sembra evitare questo strano problema: rimuovere i file di assemblaggio dal progetto e lasciare solo C. (cosa ?!)

Quindi, ecco il mio strano esempio, sto lavorando su Ubunbtu 19.04 e default gcc.

Se si compila l'eseguibile di destinazione con il file ASM (che è vuoto) mmaprestituisce un'area leggibile ed eseguibile, se si costruisce senza di allora si comporta correttamente. Vedi l'output di /proc/self/mapscui ho incorporato il mio esempio.

example.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

esempio.s : è un file vuoto!

Uscite

Con la versione ASM inclusa

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

Senza la versione inclusa di ASM

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

5
Questo è davvero strano.
— fuz,

6
Sono riuscito a riprodurlo solo con GCC (no CMake), quindi ho modificato la domanda per rendere l'esempio più minimale.
— Joseph Sible: ripristina Monica il


Potresti avere ragione, parte della sua risposta deve riguardare READ_IMPLIES_EXEC persona
— Ben Hirschberg,

Assembla i tuoi file sorgente con -Wa,--noexecstack.
— jww

Risposte:


90

Linux ha un dominio di esecuzione chiamato READ_IMPLIES_EXEC, che fa sì che PROT_READvengano date anche tutte le pagine allocate con PROT_EXEC. Questo programma ti mostrerà se è abilitato per se stesso:

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

Se lo compili insieme a un .sfile vuoto , vedrai che è abilitato, ma senza uno, sarà disabilitato. Il valore iniziale di questo deriva dalle meta-informazioni ELF nel tuo binario . Fare readelf -Wl example. Vedrai questa riga quando hai compilato senza il .sfile vuoto :

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

Ma questo quando lo hai compilato:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

Nota RWEinvece di solo RW. La ragione di ciò è che il linker presume che i file di assembly richiedano read-implica-exec a meno che non sia esplicitamente detto che non lo fanno, e se qualsiasi parte del programma richiede read-implica-exec, allora è abilitato per l'intero programma . I file assembly che GCC compila dicono che non è necessario, con questa riga (vedrai questo se compili -S):

        .section        .note.GNU-stack,"",@progbits

Inserisci quella linea example.se servirà a dire al linker che non ne ha nemmeno bisogno, e il tuo programma funzionerà come previsto.


13
Santa merda, questo è uno strano default. Immagino che la toolchain esistesse prima di noexec, e rendere noexec il default potrebbe aver rotto le cose. Ora sono curioso di sapere come altri assemblatori come NASM / YASM creano .ofile! Ma comunque, suppongo che questo sia il meccanismo che gcc -zexecstackutilizza, e perché rende non solo lo stack ma tutto eseguibile.
— Peter Cordes,

23
@Peter - Ecco perché aggiungono progetti come Botan, Crypto ++ e OpenSSL, che usano l'assemblatore -Wa,--noexecstack. Penso che sia un brutto spigolo. La perdita silenziosa di stack nx dovrebbe essere una vulnerabilità della sicurezza. La gente di Binutil dovrebbe risolverlo.
— jww

14
@jww È davvero un problema di sicurezza, strano che nessuno lo abbia segnalato prima
— Ben Hirschberg,

4
+1, ma questa risposta sarebbe molto migliore se .note.GNU-stack,"",@progbitsfosse spiegato il significato / la logica della linea - in questo momento è opaco, equivalente a "questa stringa magica di caratteri provoca questo effetto", ma la stringa sembra chiaramente che abbia una sorta di semantica.
— mtraceur,

33

In alternativa alla modifica dei file di assembly con varianti della direttiva della sezione specifica di GNU, è possibile aggiungere -Wa,--noexecstackalla riga di comando per la creazione di file di assembly. Ad esempio, guarda come lo faccio in musl's configure:

https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a

Credo che almeno alcune versioni di clang con integrato-assemblatore possano richiedere che venga passato come --noexecstack(senza il -Wa), quindi il tuo script di configurazione dovrebbe probabilmente controllare entrambi e vedere quale è accettato.

È inoltre possibile utilizzare -Wl,-z,noexecstackal momento del collegamento (in LDFLAGS) per ottenere lo stesso risultato. Lo svantaggio di ciò è che non aiuta se il tuo progetto produce .afile di libreria statici ( ) per l'uso da parte di altri software, dal momento che non controlli le opzioni di collegamento quando viene utilizzato da altri programmi.


1
Hmm ... Non sapevo che eri Rich Felker prima di leggere questo post. Perché il tuo nome visualizzato non è dalias?
— SS Anne
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.