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.