7
votes

Comment mettre en œuvre malloc déterministe

Dites que j'ai deux instances d'une application, avec les mêmes entrées et la même séquence d'exécution. Par conséquent, une instance est une instance redondante et est utilisée pour comparer des données en mémoire avec l'autre instance, en tant que type de mécanisme de détection d'erreur.

Maintenant, je veux que toutes les allocations de mémoire et les réparticiales se produisent exactement de la même manière dans les deux processus. Quel est le moyen le plus simple d'atteindre cela? Ecrivez mon propre malloc et gratuitement? Et qu'en est-il des souvenirs alloués à d'autres fonctions telles que le MMAP?


10 commentaires

Que voulez-vous dire, exactement de la même manière? Je pensais que vous avez dit avoir "la même séquence d'exécution"?


Vous voudrez peut-être que la 3ème instance de déterminer lequel des différents résultats est / sont corrects.


La séquence d'exécution est la même chose que les adresses MALLOC peuvent différer car MALLOC n'est pas déterministe.


MALLOC est déterministe tel que chaque algorithme informatique. Les ordinateurs sont vraiment mauvais pour générer des valeurs non déterministes (nombre aléatoires) par conception. Ce qui peut ne pas être déterministe est la mémoire renvoyée par anonyme mmap .


@Sylvain: D'où vous avez lu que Malloc est déterministe? Pouvez-vous donner une certaine référence?


Dupliquer de Stackoverflow.com/questions/8171006/is-malloc-deterministique


Je ne peux même pas voir comment les «mêmes intrants et la même séquence d'exécution» pourraient être démarrés et maintenus, nevez aucun problème avec MALLOC (). Le système d'exploitation planifiera le thread / s des paires de processus à des moments différents et une exécution plus diversifiée. Un processus générera une faute de page et verrouille une page pour écrire afin que l'autre processus doive attendre une révolution de disque. Des choses comme ça. Suis-je le seul à penser que les problèmes de 'Malloc ()' ne sont-ils pas en haut de la liste pour cette exigence?


@Martin: Je travaille avec l'espace d'adressage virtuel, je ne suis donc pas dérangé par les mécanismes de défaut de page sous-jacents et les révolutions de disque, etc., donc c'est complètement un non-problème!


@Metallicprimpest bien, bonne chance. Je suis content que je n'essaye pas ça! Je suppose que les deux cas sont en fait exécutés sur du matériel séparé.


@MetallicPriest de la source de la mise en œuvre de GLIBC de MALLOC ( Sourceware.org/git/?p=LLIBC.GITTAITO=BLOB ;F=MALLOC/... ). L'algorithme a l'air déterministe (non aléatoire ou de lecture de la mémoire ininitialisée). Cependant, il appelle mmap pour une vaste allocation de bloc qui n'est pas déterministe.


5 Réponses :


2
votes

Lors de l'écriture de code de fiabilité élevée, la pratique habituelle consiste à éviter que MALLOC et une autre allocation de mémoire dynamique. Un compromis parfois utilisé est de faire toute cette allocation que pendant l'initialisation du système.


0 commentaires

4
votes

Je me demande ce que vous essayez d'atteindre. Si votre processus est déterministe, le modèle d'allocation / distribution doit être identique.

La seule différence possible pourrait être l'adresse renvoyée par malloc code>. Mais vous ne devriez probablement pas en dépendre d'eux (le moyen le plus simple n'utilise pas les pointeurs comme une carte clé ou une autre structure de données). Et même alors, il ne devrait y avoir de différence que si l'allocation n'est pas effectuée via sbrk code> (l'utilisation de glibc utilise anonyme mmap code> pour les grandes allocations), ou si vous utilisez MMAP code> (Comme par défaut, l'adresse est sélectionnée par le noyau). p>

Si vous souhaitez vraiment avoir exactement la même adresse, une option est d'avoir un grand tampon statique et d'écrire une vue personnalisée. Allocator qui utilise la mémoire de ce tampon. Cela a l'inconvénient de vous obliger à connaître à l'avance la quantité maximale de mémoire dont vous aurez besoin. Dans un exécutable non-tartre ( GCC -FNO-PIE -NO-PIE CODE>), un tampon statique aura la même adresse à chaque fois. Pour une exécutable à tarte, vous pouvez désactiver le noyau Adresse Space Disposition Randomisation pour le chargement de programmes. Dans une bibliothèque partagée, désactivant l'ASLR et l'exécution du même programme deux deux fois, il convient de conduire aux mêmes choix par la liaison dynamique pour où cartographier les bibliothèques. P>

Si vous ne savez pas avant la taille maximale de la mémoire de la mémoire. Vous souhaitez utiliser ou si vous ne voulez pas recompiler chaque fois cette taille de taille, vous pouvez également utiliser mmap code> pour mapper un grand tampon anonyme à une adresse fixe. Il vous suffit de passer la taille du tampon et de l'adresse à utiliser comme paramètre à votre processus et utilisez la mémoire renvoyée pour implémenter votre propre malloc code> sur le dessus de celui-ci. P>

static void* malloc_buffer = NULL;
static size_t malloc_buffer_len = 0;

void* malloc(size_t size) {
    // Use malloc_buffer & malloc_buffer_len to implement your
    // own allocator. If you don't read uninitialized memory,
    // it can be deterministic.
    return memory;
}

int main(int argc, char** argv) {
    size_t buf_size = 0;
    uintptr_t buf_addr = 0;
    for (int i = 0; i < argv; ++i) {
        if (strcmp(argv[i], "--malloc-size") == 0) {
            buf_size = atoi(argv[++i]);
        }
        if (strcmp(argv[i], "--malloc-addr") == 0) {
            buf_addr = atoi(argv[++i]);
        }
    }

    malloc_buffer = mmap((void*)buf_addr, buf_size, PROT_WRITE|PROT_READ,
                         MAP_FIXED|MAP_PRIVATE, 0, 0);
    // editor's note: omit MAP_FIXED since you're checking the result anyway
    if (malloc_buffer == MAP_FAILED || malloc_buffer != (void*)but_addr) {
        // Could not get requested memory block, fail.
        exit(1);
    }

    malloc_size = buf_size;
}


3 commentaires

"Si vous voulez vraiment avoir exactement la même adresse, une option est d'avoir un grand tampon statique et d'écrire un allocator personnalisé qui utilise la mémoire de ce tampon.". C'est exactement ce que je faisais jusqu'à présent, mais je veux maintenant passer à une approche plus générale.


@Sylvain. Apparemment métallisé veut faire un mécanisme de point de contrôle. Mais il est très secret.


Ce n'est pas seulement le noyau qui décide d'ASLR ou non: l'exécutable doit l'appuyer. Si vous souhaitez que des adresses statiques soient les mêmes à chaque fois, construisez simplement une exécutable sans tarte avec GCC -FNO-PIE -NO-PIE . (Les distributions modernes ont leur défaut de la GCC pour faire des tartes.) Voir Adresses absolues 32 bits N'est plus autorisée dans X86-64 Linux? . Mais si votre code est dans une bibliothèque partagée, vous devez désactiver l'ASLR avec une SYSCTL de noyau ou quelque chose. Je pense que la liaison dynamique utilise simplement mmap , le noyau lui-même ne "sait pas" c'est une bibliothèque étant mappée.



4
votes

Qu'est-ce qui n'est pas déterministe n'est pas seulement MALLOC mais MMAP (le système de système de base pour obtenir plus d'espace mémoire; ce n'est pas une fonction, c'est un appel système < / a> est donc élémentaire ou atomique du point de vue de l'application; vous ne pouvez donc pas le réécrire dans l'application) à cause de Adresse Space Disposition Randomisation sur Linux.

Vous pouvez le désactiver avec xxx

comme root, ou à travers SYSCTL .

Si vous ne désactivez pas l'adresse SPATE DE LA DISPOSITION Randomisation Vous êtes bloqué.

Et vous avez posé une question similaire précédemment , où j'ai expliqué que votre MALLOC -S ne sera pas toujours déterministe.

Je pense toujours que pour certaines applications pratiques, malloc ne peut pas être déterministe. Imaginez par exemple un programme ayant une table de hash à clé par le PID -S de l'enfant le processus lancé. La collision dans cette table ne sera pas la même dans tous vos processus, etc.

Je pense que vous ne réussirez pas à faire Malloc déterministe dans votre sens, quoi que ce soit Essayez (sauf si vous ne vous limitez pas à une classe d'applications très étroite au point de contrôle, si étroit que votre logiciel ne sera pas très utile).


4 commentaires

Donc, vous dites que ce n'est que non déterministe dû à ASLR et sinon c'est déterministe? Es-tu sûr? Est-ce que cela s'applique à la fois pour SBRK et MMAP? Comment pouvez-vous désactiver ASLR si vous n'avez pas de droits d'administration?


Je ne sais pas sur sbrk mais je crois que oui. AFAIK, ASLR exige root Privilège pour être désactivé (sinon c'est un énorme trou de sécurité).


Ya mais quelque part j'ai lu Setarch x86_64 -r peut être utilisé pour une session.


Je ne comprends pas comment avoir une table de hachage indexée par pid fera malloc non déterministe. Il rendra le processus non déterministe dans son utilisation de malloc , mais la fonction elle-même est toujours déterministe. J'ai travaillé sur un projet qui nécessitait le déterminisme et si vous prenez soin de ne pas introduire de non-déterminisme de l'extérieur (comme vous l'avez dit PID d'un autre processus ou à l'aide de pointeur a haché) et n'utilisez pas X87 FPU (mais utilisez plutôt SSE), votre programme peut être déterministe.



-1
votes

Vous pouvez utiliser la mémoire partagée pour stocker vos données. Il sera accessible des deux processus et vous pouvez le remplir de manière déterministe.


1 commentaires

-1, oh commun s'il vous plaît pensez avant d'écrire. Tout le but de faire une exécution redondante est de pouvoir comparer les mémoires des deux processus exécutant dans leur propre espace d'adresses.



4
votes

Il suffit de mettre, car d'autres ont déclaré: si l'exécution des instructions de votre programme est déterministe, la mémoire renvoyée par malloc () sera déterministe. Cela suppose que la mise en œuvre de votre système n'a pas d'appel à aléatoire () ou quelque chose à cet effet. Si vous n'êtes pas sûr, lisez le code ou la documentation du Malloc .

C'est à l'exception possible de l'ASLR, car d'autres ont également déclaré. Si vous n'avez pas de privilèges root, vous pouvez le désactiver par processus via la personnalité (2) syscall et le paramètre addr_no_randomize. Voir ici pour plus d'informations sur les personnalités. < / p>

Edit: Je devrais aussi dire que si vous n'êtes pas au courant: Ce que vous faites est appelé bisimulation et est une technique bien étudiée. Si vous ne connaissiez pas la terminologie, cela pourrait vous aider à avoir ce mot clé pour la recherche.


0 commentaires