web-dev-qa-db-fra.com

Autorisation d'exécution inattendue de mmap lorsque les fichiers d'assemblage sont inclus dans le projet

Je me tape la tête contre le mur avec ça.

Dans mon projet, lorsque j'alloue de la mémoire avec mmap le mappage (/proc/self/maps) montre qu'il s'agit d'une région lisible et exécutable malgré J'ai demandé uniquement de la mémoire lisible.

Après avoir examiné Strace (qui avait l'air bien) et d'autres débogages, j'ai pu identifier la seule chose qui semble éviter cet étrange problème: supprimer les fichiers Assembly du projet et ne laisser que du C. pur (quoi?!)

Voici donc mon étrange exemple, je travaille sur Ubunbtu 19.04 et gcc par défaut.

Si vous compilez l'exécutable cible avec le fichier ASM (qui est vide) alors mmap retourne une région lisible et exécutable, si vous construisez sans alors il se comportera correctement. Voir la sortie de /proc/self/maps que j'ai intégré dans mon exemple.

exemple.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: est un fichier vide!

Sorties

Avec la version incluse ASM

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

Sans la version incluse ASM

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

Linux a un domaine d'exécution appelé READ_IMPLIES_EXEC, Ce qui fait que toutes les pages allouées avec PROT_READ Reçoivent également PROT_EXEC. Ce programme vous montrera si c'est activé pour lui-même:

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

Si vous compilez cela avec un fichier .s Vide, vous verrez qu'il est activé, mais sans un, il sera désactivé. La valeur initiale de ceci provient des méta-informations ELF dans votre binaire . Faites readelf -Wl example. Vous verrez cette ligne lorsque vous aurez compilé sans le fichier .s Vide:

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

Mais celui-ci lorsque vous avez compilé avec:

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

Remarque RWE au lieu de simplement RW. La raison en est que l'éditeur de liens suppose que vos fichiers d'assembly nécessitent read-implique-exec à moins qu'il ne soit explicitement dit qu'ils ne le font pas, et si une partie de votre programme nécessite read-implique-exec, il est activé pour l'ensemble de votre programme . Les fichiers d'assembly que GCC compile lui indiquent qu'il n'en a pas besoin, avec cette ligne (vous verrez ceci si vous compilez avec -S):

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

Mettez cette ligne dans example.s, Et cela servira à dire à l'éditeur de liens qu'il n'en a pas besoin non plus, et votre programme fonctionnera alors comme prévu.

Comme alternative à la modification de vos fichiers Assembly avec des variantes de directive de section spécifiques à GNU, vous pouvez ajouter -Wa,--noexecstack à votre ligne de commande pour créer des fichiers d'assemblage. Par exemple, voyez comment je le fais dans configure de musl:

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

Je crois qu'au moins certaines versions de clang avec l'assembleur intégré peuvent nécessiter qu'il soit passé comme --noexecstack (sans le -Wa), donc votre script de configuration devrait probablement vérifier les deux et voir lequel est accepté.

Vous pouvez aussi utiliser -Wl,-z,noexecstack au moment du lien (dans LDFLAGS) pour obtenir le même résultat. L'inconvénient est que cela n'aide pas si votre projet produit de l'électricité statique (.a) fichiers de bibliothèque à utiliser par d'autres logiciels, car vous ne contrôlez pas les options de temps de liaison quand il est utilisé par d'autres programmes.