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


94

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

Dans mon projet, lorsque j'alloue de la mémoire avec mmaple mapping ( /proc/self/maps), cela montre que c'est une région lisible et exécutable bien que je n'aie demandé que 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 d'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), puis mmapretourne une région lisible et exécutable, si vous construisez sans, il se comportera correctement. Voir la sortie /proc/self/mapsdont 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!

Les sorties

Avec la version ASM incluse

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 

5
C'est vraiment bizarre.
fuz

6
J'ai réussi à reproduire cela avec seulement GCC (pas de CMake), j'ai donc édité la question pour rendre l'exemple plus minimal.
Joseph Sible-Reinstate Monica


Vous avez peut-être raison, une partie de la réponse doit être autour du personnage de READ_IMPLIES_EXEC
Ben Hirschberg

Assemblez vos fichiers source avec -Wa,--noexecstack.
2019 à 4h59

Réponses:


90

Linux a un domaine d'exécution appelé READ_IMPLIES_EXEC, qui donne également toutes les pages allouées PROT_READà 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 .sfichier vide , vous verrez qu'il est activé, mais sans celui-ci, 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 .sfichier 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

Notez RWEau lieu de juste 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 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.


13
Putain de merde, c'est un défaut étrange. Je suppose que la chaîne d'outils existait avant noexec, et faire de noexec la valeur par défaut aurait pu casser les choses. Maintenant, je suis curieux de voir comment d'autres assembleurs comme NASM / YASM créent des .ofichiers! Mais de toute façon, je suppose que c'est le mécanisme qui gcc -zexecstackutilise, et pourquoi il rend non seulement la pile mais tout exécutable.
Peter Cordes

23
@Peter - C'est pourquoi des projets comme Botan, Crypto ++ et OpenSSL, qui utilisent l'assembleur, ajoutent -Wa,--noexecstack. Je pense que c'est un tranchant très méchant. La perte silencieuse de piles nx devrait être une vulnérabilité de sécurité. Les gens de Binutil devraient le réparer.
jww

14
@jww C'est en effet un problème de sécurité, étrange que personne ne l'ait signalé auparavant
Ben Hirschberg

4
+1, mais cette réponse serait bien meilleure si la signification / logique de la ligne .note.GNU-stack,"",@progbitsétait expliquée - en ce moment, elle est opaque, équivalente à "cette chaîne magique de caractères provoque cet effet", mais la chaîne semble clairement avoir une sorte de sémantique.
mtraceur

33

Au lieu de modifier vos fichiers d'assemblage 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 Musl's configure:

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é sous --noexecstack(sans le -Wa), donc votre script de configuration devrait probablement vérifier les deux et voir lequel est accepté.

Vous pouvez également utiliser -Wl,-z,noexecstackau moment du lien (in LDFLAGS) pour obtenir le même résultat. L'inconvénient de cela est que cela n'aide pas si votre projet produit des .afichiers de bibliothèque static ( ) à 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.


1
Hmm ... Je ne savais pas que vous étiez Rich Felker avant de lire ce post. Pourquoi vos dalias de nom d'affichage ne sont-ils pas?
SS Anne
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.