Unerwartete Ausführungsberechtigung von mmap, wenn Baugruppendateien im Projekt enthalten sind




linux assembly (2)

Ich schlage damit meinen Kopf gegen die Wand.

In meinem Projekt zeigt die Zuordnung ( /proc/self/maps ), wenn ich Speicher mit mmap /proc/self/maps , dass es sich um eine lesbare und ausführbare Region handelt, obwohl ich nur lesbaren Speicher angefordert habe.

Nachdem ich mir Strace (was gut aussah) und andere Fehlerbehebungsmaßnahmen angeschaut hatte, konnte ich das einzige identifizieren, das dieses seltsame Problem zu vermeiden scheint: Assembly-Dateien aus dem Projekt entfernen und nur reines C. belassen (was ?!)

Also hier ist mein seltsames Beispiel, ich arbeite an Ubunbtu 19.04 und Standard gcc.

Wenn Sie die ausführbare Zieldatei mit der ASM-Datei kompilieren (die leer ist), gibt mmap eine lesbare und ausführbare Region zurück, wenn Sie sie ohne kompilieren, verhält sie sich korrekt. Siehe die Ausgabe von /proc/self/maps die ich in mein Beispiel eingebettet habe.

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;
}

example.s : Ist eine leere Datei!

Ausgänge

Mit der mitgelieferten ASM-Version

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

Ohne die mitgelieferte ASM-Version

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

Als Alternative zum Ändern Ihrer Assembly-Dateien mit GNU-spezifischen Section-Direktiven-Varianten können Sie Ihrer Befehlszeile -Wa,--noexecstack zum -Wa,--noexecstack von Assembly-Dateien hinzufügen. Sehen Sie sich zum Beispiel an, wie ich es in musls configure mache:

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

Ich glaube, dass zumindest einige Versionen von Clang mit integriertem Assembler erfordern, dass es als --noexecstack (ohne das -Wa ) übergeben wird. -Wa sollte Ihr configure-Skript wahrscheinlich beide prüfen und sehen, welches akzeptiert wird.

Sie können auch -Wl,-z,noexecstack zur Verbindungszeit (in LDFLAGS ) verwenden, um das gleiche Ergebnis zu erhalten. Dies hat den Nachteil, dass es nicht hilft, wenn Ihr Projekt statische ( .a ) Bibliotheksdateien zur Verwendung durch andere Software erstellt, da Sie dann die Verbindungszeitoptionen nicht steuern können, wenn sie von anderen Programmen verwendet werden.


Linux hat eine Ausführungsdomäne namens READ_IMPLIES_EXEC , wodurch allen mit PROT_READ zugewiesenen Seiten auch PROT_EXEC . Dieses Programm zeigt Ihnen, ob es für sich selbst aktiviert ist:

#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;
}

Wenn Sie dies zusammen mit einer leeren .s Datei kompilieren, sehen Sie, dass sie aktiviert ist, aber ohne eine wird sie deaktiviert. Der Anfangswert hierfür stammt aus den ELF-Metainformationen in Ihrer Binärdatei . Do readelf -Wl example . Diese Zeile wird .s wenn Sie ohne die leere .s Datei kompiliert haben:

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

Aber dies hier, wenn Sie damit kompiliert haben:

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

Beachten Sie RWE statt nur RW . Der Grund dafür ist, dass der Linker davon ausgeht, dass Ihre Assembly-Dateien read-implies-exec erfordern, es sei denn, es wird ausdrücklich darauf hingewiesen, dass dies nicht der Fall ist. Wenn ein Teil Ihres Programms read-implies-exec erfordert, ist es für Ihr gesamtes Programm aktiviert . Die Assembly-Dateien, die von GCC kompiliert werden, zeigen an, dass dies nicht erforderlich ist (dies wird angezeigt, wenn Sie mit -S kompilieren):

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

Wenn Sie diese Zeile in example.s , wird dem Linker mitgeteilt, dass er sie ebenfalls nicht benötigt, und Ihr Programm wird dann wie erwartet funktionieren.





mmap