1. Vue d'ensemble
Le traitement DFT (Dynamic Data Flow Tracing) marque et suit les données d'intérêt au fur et à mesure qu'elles se propagent pendant l'exécution du programme. La DFT a été utilisée à plusieurs reprises par divers outils à des fins diverses, notamment pour prévenir les attaques de script 0day et intersites, détecter et prévenir les fuites d'informations et analyser les logiciels légitimes et malveillants. Nous avons proposé libdft, un cadre DFT dynamique, qui n'est pas aussi rapide, réutilisable que les travaux précédents et qui peut être utilisé avec des logiciels et du matériel de base. Libdft fournit une API pour créer des outils qui prennent en charge la DFT. Fonctionne sur des fichiers binaires non modifiés et s'exécute sur des systèmes d'exploitation et du matériel à usage général, facilitant ainsi la recherche et le prototypage rapide.
Nous avons exploré différentes façons de mettre en œuvre les aspects de bas niveau du suivi des données de niveau instruction, introduit une mémoire fantôme plus efficace et 64 bits, et identifié (et évité) les déficiences courantes qui ont causé une surcharge de performances excessive précédemment étudiée. Nous utilisons des applications du monde réel avec de grandes bases de code (telles que des serveurs Apache et MySQL) et des navigateurs Web Firefox pour évaluer libdft. Nous utilisons également une série de références et d'utilitaires pour comparer libdft avec des systèmes similaires. Nos résultats montrent qu'elle est au moins aussi rapide que la solution précédente, voire plus rapide, et pour autant que nous le sachions, nous sommes les premiers à évaluer à cette profondeur le surcoût de performance d'une implémentation DFT dynamique rapide. Enfin, libdft peut être utilisé gratuitement comme logiciel open source.
Conditions générales de conception, de performance, de sécurité
Mots-clés suivi du flux de données, détection binaire dynamique, analyse des souillures, détection des fuites d'informations, prévention des exploits de vulnérabilité
1. Introduction
Le traçage dynamique de flux de données (DFT), également connu sous le nom de traçage de flux d'informations, est une technique bien connue qui gère le marquage et le suivi des données "intéressantes" propagées pendant l'exécution du programme. La DFT a de nombreuses utilisations, telles que l'analyse du comportement des logiciels malveillants, le renforcement des logiciels contre les attaques 0day (par exemple, les débordements de tampon, le formatage des chaînes), la détection et la prévention des fuites d'informations, et même le débogage des erreurs de configuration des logiciels. D'un point de vue architectural, il a été intégré dans un émulateur système complet et un moniteur de machine virtuelle, utilisant le détecteur binaire dynamique pour améliorer le fichier binaire non modifié et utilisant la conversion de code source à source pour l'ajouter au code source Dans la bibliothèque. Il existe également des suggestions pour l'implémenter dans le matériel, mais elles ont peu d'intérêt pour les fournisseurs de matériel.
Des études antérieures ont utilisé la DFT pour étudier l'applicabilité de la technologie dans des domaines d'intérêt spécifiques, ce qui a entraîné leurs propres problèmes spécifiques et des implémentations temporaires de la DFT basée sur logiciel, qui ont toutes été affectées par un ou plusieurs des problèmes suivants: frais généraux élevés, possibles La réutilisation est faible (c.-à-d. Qu'ils sont propres à un problème) et l'applicabilité est limitée (c.-à-d. Qu'ils ne s'appliquent pas aux logiciels existants). Par exemple, LIFT [22] et Minemu [2] utilisent DFT pour détecter les attaques de sécurité. Bien que rapides, ils ne prennent pas en charge les applications multithreads (première conception). LIFT ne s'applique qu'aux binaires 64 bits, tandis que Minemu ne s'applique qu'aux binaires 32 bits, et sa conception nécessite de nombreuses modifications pour prendre en charge l'architecture 64 bits. Plus important encore, ils se concentrent sur un domaine à problème unique et ne peuvent pas être facilement modifiés pour être utilisés dans d'autres domaines.
Une mise en œuvre plus précise et personnalisable de la DFT à grain fin n'a pas non plus fourni un cadre de DFT pratique et réutilisable pour la communauté des chercheurs. Par exemple, Dytan se concentre sur la présentation d'outils DFT configurables qui prennent en charge les données et contrôlent les dépendances de flux. Malheureusement, sa polyvalence est coûteuse, même lors de l'exécution de petits programmes qui n'ont que des dépendances de flux de données (les dépendances de flux de contrôle peuvent affecter davantage les performances). Par exemple, Dytan a signalé un ralentissement de 30x lors de l'utilisation de la compression gzip, tandis que LIFT a signalé moins de 10x. Bien que les expériences ne soient pas directement comparables, la différence significative de performances indique que la conception de Dytan n'est pas adaptée aux faibles frais généraux.
Cet article estime que l'implémentation DFT dynamique réelle doit résoudre les trois problèmes énumérés ci-dessus, elle doit donc être rapide, réutilisable et applicable au matériel et aux logiciels de base en même temps. Nous avons introduit libdft, un méta-outil sous la forme d'une bibliothèque partagée qui utilise le framework de détection binaire dynamique Pin d'Intel pour implémenter la DFT dynamique [16]. Les performances de libdft sont comparables ou meilleures que les travaux précédents, ce qui entraîne une vitesse d'utilitaire de ligne de commande comprise entre 1,14x et 6,03x, et il peut également exécuter de grandes applications de serveur telles qu'Apache et MySQL avec un surcoût de 1,25x et 4,83x. Entre. De plus, il est polyvalent et réutilisable, peut fournir une large gamme d'API et peut être utilisé pour implémenter des outils pilotés par DFT. Enfin, il fonctionne sur le système des produits de base. Notre implémentation actuelle fonctionne avec des binaires x86 sous Linux, et nous prévoyons de l'étendre pour qu'elle fonctionne sur une architecture 64 bits et un système d'exploitation Windows (OS). libdft introduit une mémoire cachée efficace de 64 bits, ce qui représente l'une des limitations les plus sérieuses des premiers travaux, car la structure de la mémoire cachée plate impose une surcharge d'espace mémoire ingérable sur les systèmes 64 bits et est gérée dynamiquement La structure introduit des pertes de performances élevées. Plus important encore, libdft prend en charge les applications multi-processus et multi-thread, garantissant la mémoire contre les conditions de concurrence en compromettant la mémoire (voir la section 7), et il ne nécessite pas de modification de programme ou du système d'exploitation sous-jacent.
Les contributions de cet article peuvent être résumées comme suit:
- Nous avons discuté de la conception et de la mise en œuvre d'une bibliothèque DFT partagée rapide et réutilisable pour les logiciels commerciaux. Plus précisément, nous étudions et déterminons les causes potentielles de la dégradation des performances provoquée par les outils DFT précédents et proposons de minimiser sa conception. Nous traitons le problème du point de vue du système et essayons de répondre aux questions suivantes: Quelles sont les limites de performances de cet outil DFT? Quelles pratiques les praticiens et les développeurs de systèmes doivent-ils éviter? Quelle est la source des frais généraux? Nous avons également proposé une série de nouvelles optimisations pour améliorer encore les performances de la DFT.
- Nous utilisons des applications du monde réel qui incluent des logiciels complexes et volumineux (tels que les serveurs Apache et MySQL et les navigateurs Web Firefox) pour évaluer les performances de libdft. libdft atteint des performances similaires ou meilleures que les travaux précédents et est applicable à un ensemble de logiciels plus large. De plus, notre évaluation approfondie a établi une série de limites sur les performances DFT. À notre connaissance, nous sommes les premiers à mener une évaluation aussi approfondie du cadre DFT.
- Nous avons montré le développement d'outils pilotés par libdft (ie libdft-DTA) pour montrer la réutilisabilité et les fonctions de libdft. libdft-DTA effectue une analyse dynamique des taches (DTA) pour détecter les attaques 0day similaires à TaintCheck, Eudaemon et LIFT. Nous avons montré que notre API polyvalente peut être utilisée pour développer un outil complexe dans environ 450 lignes de code C ++.
- Notre implémentation est disponible gratuitement en tant que bibliothèque partagée, et peut être utilisée pour développer une transparence (c'est-à-dire sans aucune modification de l'application ou du système d'exploitation sous-jacent) pour utiliser le service DFT. Les développeurs peuvent utiliser l'API fournie pour définir facilement les données d'intérêt, puis capturer leur utilisation à tout moment. De cette façon, libdft facilite la recherche et le prototypage rapide en permettant aux utilisateurs potentiels de se concentrer sur la résolution de problèmes spécifiques (par exemple, la détection de fuites d'informations ou la mauvaise configuration des applications) plutôt que de traiter les moindres détails du cadre de suivi du flux d'informations.
Le reste de cet article est organisé comme suit: La section 2 présente la DFT et traite des différences entre les méthodes DFT dynamiques et statiques. Nous avons introduit libdft dans la section 3 et détaillé son implémentation dans la section 4. La section 5 traite de l'utilisation de libdft dans divers outils et montre son API en créant un outil DTA, que nous avons évalué avec libdft dans la section 6. La section 7 examine les limites de la mise en œuvre actuelle et les considérations futures. Voir la section 8 pour les travaux connexes et la section 9 pour les conclusions.
2. Suivi du flux de données La
DFT a toujours été un sujet de recherche populaire, principalement utilisé pour mettre en œuvre un flux d'informations sécurisé et identifier l'utilisation illégale de données. Dans les travaux antérieurs, il est souvent appelé suivi du flux d'informations (IFT) [24] ou analyse des taches. Ce travail définit la DFT comme: "le processus de suivi précis du flux de données sélectionné lors de l'exécution de l'ensemble du programme ou du système." Les caractéristiques de ce processus sont de trois aspects, nous allons essayer de clarifier à l'aide des extraits de code illustrés à la figure 1.
Source de données: une source de données est un emplacement de programme ou de mémoire, où les données d'intérêt pénètrent généralement dans le système après l'exécution d'une fonction ou d'un appel système. Les données de ces sources sont marquées et suivies. Par exemple, si nous définissons le fichier comme source, l'appel de lecture de la figure 1 entraînera le balisage des données et leur transmission.
Suivi des données: Pendant l'exécution du programme, les données marquées sont suivies lorsqu'elles sont copiées et modifiées par les instructions du programme. Considérez l'extrait de code (a) dans la figure 1, où les données ont été marquées à la ligne 3. La boucle while suivante calcule une somme de contrôle simple (OU exclusif de tous les octets dans les données) et stocke le résultat dans la somme. Dans ce cas, il existe une dépendance de flux de données entre le csum et les données, car le premier dépend directement du second.
En revanche, l'autorisation en (b) est indirectement affectée par la valeur phash, qui à son tour dépend de la réussite. Ceci est souvent appelé la dépendance du flux de contrôle Dans ce travail, nous ne considérons pas le cas du flux de données implicite, ce qui est cohérent avec les travaux précédents sur le sujet [18,24]. Dytan a pris des dispositions pour traiter ce type de dépendance de flux de contrôle de manière conditionnelle, mais a conclu que bien qu'elles soient utiles dans certains domaines, elles entraînent souvent une augmentation de la quantité de données étiquetées et des dépendances de données incorrectes [ 6]. Les travaux en cours visent à résoudre ces problèmes [15].
Récepteur de données: un récepteur de données est également un programme ou un emplacement de mémoire qui peut vérifier la présence de données marquées et est généralement utilisé pour vérifier ou appliquer le flux de données. Par exemple, certaines zones de mémoire et certains paramètres de fonction peuvent ne pas autoriser les données de balise. Considérez à nouveau l'extrait de code (a) dans la figure 1, où la ligne 7 écrit csum dans le fichier. Si le fichier est défini comme récepteur de données, l'utilisation de l'écriture pour écrire un csum peut déclencher une opération définie par l'utilisateur
DFT dynamique et statique: l'exécution de la DFT nécessite une mémoire d'étiquette de données supplémentaire. De plus, le programme lui-même doit être étendu en utilisant la logique de propagation des étiquettes et la logique de marquage et d'inspection des données à la source et au puits, respectivement. Le code supplémentaire est souvent appelé code d'instrumentation et peut être injecté de manière statique (par exemple, lors du développement du code source et lors de la compilation / du chargement), ou dynamiquement à l'aide de la virtualisation ou de l'instrumentation binaire dynamique (DBI). Les systèmes statiques appliquent la DFT en recompilant le logiciel à l'aide d'un compilateur modifié [25] ou d'un moteur de conversion de source à source [28]. En revanche, les fichiers binaires dynamiques peuvent être directement appliqués aux fichiers binaires non modifiés, y compris les logiciels disponibles dans le commerce [22,27,31]. Dans les deux cas, le logiciel a besoin de tests approfondis pour associer les données à une certaine étiquette et injecter une logique qui affirme l'étiquette à la source, les propage en fonction des dépendances de données définies par la sémantique du programme, et enfin vérifie Le fait que le récepteur dispose d'une solution dynamique de données de balise est beaucoup plus lent qu'une solution statique, mais présente l'avantage d'être immédiatement et progressivement applicable aux logiciels déployés.
![](https://img-blog.csdnimg.cn/20190504210707857.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5nMTQ5MTY=,size_16,color_FFFFFF,t_70)
3. Conception
Nous avons conçu libdft à utiliser avec le framework Pin DBI pour faciliter la création de Pintools à l'aide de DFT dynamique. En bref, Pin se compose d'une bibliothèque de machine virtuelle (VM) et d'un injecteur qui connecte la VM à un processus déjà en cours d'exécution ou démarre son propre nouveau processus. Pintools est une bibliothèque partagée qui utilise l'API étendue de Pin pour vérifier et modifier les binaires de niveau instruction. libdft est également une bibliothèque, Pintools peut appliquer de manière transparente la DFT à grain fin sur les fichiers binaires exécutés sur Pin. Plus important encore, il fournit sa propre API (voir la section 5), permettant aux auteurs d'outils de personnaliser facilement les sources et les récepteurs libdft en spécifiant les données, et même de modifier les stratégies de propagation des étiquettes.
Lorsque l'utilisateur se connecte à un processus qui est déjà en cours d'exécution ou utilise Pintool qui prend en charge libdft pour démarrer un nouveau processus, l'injecteur injecte d'abord le temps d'exécution de Pin, puis passe le contrôle à l'outil. Les outils qui activent libdft peuvent utiliser trois types d'emplacements comme sources ou récepteurs de données: instructions de programme, appels de fonction et appels système. Il peut être installé en appelant des rappels lorsqu'il rencontre une instruction, ou en "cliquant" sur ces emplacements lors d'un appel de fonction ou système. Ces rappels définis par l'utilisateur pilotent le processus DFT en marquant ou en démarquant les données et en surveillant ou en forçant le flux de données. La figure 2 illustre l'image mémoire d'un processus exécuté sous Pintool qui prend en charge libdft. La case en surbrillance indique où l'auteur de l'outil peut installer le rappel. Par exemple, l'utilisateur peut marquer le contenu du tampon renvoyé par l'appel système lu (comme illustré dans l'exemple de la figure 1) et vérifier si l'opérande de l'instruction d'appel indirect est marqué (par exemple, le registre eax de la figure 2). .
3.1. Balises de données
libdft stocke les balises de données dans tagmap. Tagmap contient une structure de données à l'échelle du processus (mémoire fantôme) pour le stockage des données stockées dans les balises de mémoire, et une structure spécifique au thread pour l'enregistrement de résider La marque des données dans le registre CPU. Le format d'étiquette stocké dans le tagmap est principalement déterminé par deux facteurs: (a) la granularité de l'étiquette, et (b) la taille de l'étiquette. Marquage de la granularité En principe, nous pouvons marquer des unités de données aussi petites qu'un bit ou en tant que blocs de mémoire contigus plus grands. Le premier nous permet d'effectuer une DFT à grain très fin et précis, tout en utilisant une granularité plus grande signifie que le suivi des données sera plus rugueux et plus sujet aux erreurs. Par exemple, pour la granularité au niveau de la page, le déplacement d'un seul octet (marqué) vers une position non marquée entraînera la marque à contenir la page entière de la cible, "polluant" ainsi les données adjacentes. Cependant, la sélection de balises très fines nécessite un coût élevé car le stockage des balises nécessite plus d'espace de stockage (par exemple, avec des balises au niveau bit, un seul octet nécessite 8 balises et un registre 32 bits nécessite 32 balises). Plus important encore, la logique de propagation des étiquettes devient compliquée car la dépendance des données est également plus compliquée (par exemple, envisagez d'ajouter deux nombres de 32 bits qui ne marquent qu'une partie de leurs bits). libdft utilise une granularité au niveau octet, car dans la plupart des architectures, les octets sont le plus petit bloc de mémoire adressable. Notre choix nous permet de fournir un suivi suffisamment fin pour la plupart des applications pratiques, et nous pensons qu'il établit un équilibre entre la convivialité et les performances [21].
Les tailles de balise marquent la granularité orthogonalement et les balises plus grandes sont plus polyvalentes car elles permettent à différents types de données d'être balisés de manière unique (par exemple, chaque octet peut être balisé avec un nombre unique de 32 bits). Malheureusement, les étiquettes plus grandes nécessitent une logique de propagation complexe et plus d'espace de stockage. Libdft propose deux tailles d'étiquette différentes: (a) utilisées pour associer jusqu'à 8 valeurs ou couleurs différentes à chaque octet d'étiquette Marque d'octet (chaque bit représente une classe de marque différente), et (b) marque d'unité (c'est-à-dire que les données sont marquées) ou non). Le premier permet des outils de suivi et d'analyse plus sophistiqués, tandis que le second permet des outils qui ne nécessitent que des balises binaires pour économiser de la mémoire.
3.2 Propagation des balises La propagation des
balises est réalisée à l'aide de l'API de Pin, qui est utilisée pour détecter et analyser le processus cible. Dans la terminologie de Pin, la détection fait référence à la tâche de vérifier les instructions binaires d'un programme pour déterminer où les routines d'analyse doivent être insérées. Par exemple, libdft vérifie (déclare librement) chaque instruction de programme qui déplace ou combine des données pour déterminer les dépendances de données. D'un autre côté, l'analyse fait référence à la routine ou au code réel qui est amélioré avant, après ou à la place de l'exécution du code d'origine. Dans notre exemple, nous injectons du code d'analyse qui implémente une logique de propagation d'étiquette basée sur les dépendances de données observées pendant le processus de détection.
Le code d'origine et les routines d'analyse de libdft sont convertis par le compilateur en temps réel (JIT) de Pin et utilisés pour générer le code en cours d'exécution. Cela se produit immédiatement avant la première exécution de la séquence de code et le résultat est placé dans le cache de code (également illustré sur la figure 2) pour éviter de répéter le processus pour la même séquence de code. Le code que nous injectons est exécuté avant les instructions d'application, et les données sont suivies lors de leur copie entre les registres et entre les registres et la mémoire pour obtenir une TFD à grain fin. Le processus cible VMensures de Pin s'exécute entièrement à partir du cache de code, en interprétant toutes les instructions qui ne peuvent pas être exécutées en toute sécurité (par exemple, les branches indirectes). De plus, une série d'optimisations ont été appliquées, comme le suivi des liens et la réallocation des registres pour améliorer les performances [16].
Enfin, libdft permet à l'outil de modifier la stratégie de propagation de balise par défaut en enregistrant son propre rappel de détection via son API pour obtenir les instructions d'intérêt. De cette façon, les auteurs d'outils peuvent personnaliser les balises de données en fonction de leurs besoins et, dans certains cas, annuler la propagation des balises ou suivre d'autres instructions non traitées.
3.3 Le défi de la DFT dynamique rapide
Afin de réduire les frais généraux de libdft, nous avons soigneusement étudié comment les frameworks DBI (tels que Pin) identifient les pratiques de développement à éviter. Les frais généraux de Pin dépendent principalement de la taille du code d'analyse injecté, mais en raison de la structure du code lui-même, il peut généralement être plus élevé que prévu. Plus précisément, les registres fournis par l'architecture sous-jacente seront utilisés pour exécuter le code d'application et le code qui implémente la logique DFT, forçant ainsi le cadre DBI à déborder les registres (c'est-à-dire, enregistrer son contenu en mémoire et les restaurer plus tard), chaque fois que vous analysez l'exemple Lorsque le programme doit utiliser les registres alloués. Par conséquent, plus le code est complexe, plus il y a de registres.
De plus, en raison de certains effets secondaires, certains types d'instructions doivent être évités. Par exemple, le débordement du registre EFLAGS dans l'architecture x86 en termes de cycles de traitement est coûteux et est exécuté par des instructions dédiées (PUSHF, PUSHFD). Par conséquent, les instructions du code d'analyse qui modifie ce registre doivent être soigneusement incluses. Plus important encore, les opérations de test et de branchement doivent être évitées car elles conduisent à du code non en ligne. En particulier, chaque fois que le code DFT contient une instruction de branchement, le moteur JIT de Pin émet un appel de fonction à la routine d'analyse correspondante, plutôt que le code du code en ligne et les instructions de l'application. Il est difficile d'imposer de telles restrictions à la mise en œuvre de tout outil DFT dynamique. Notre implémentation prend en compte ces problèmes avec Pin pour obtenir de bonnes performances.
La conception de libdft fournit la base d'un cadre qui répond aux trois attributs énumérés dans la section 1. En considérant les limitations discutées ci-dessus, nous avons obtenu un faible temps système. De plus, la vaste API de libdft la rend réutilisable car elle permet aux utilisateurs de la personnaliser pour divers domaines, tels que la sécurité, la confidentialité, l'analyse de programme et le débogage. Enfin, le dernier attribut est atteint en utilisant une plate-forme DBI mature, non expérimentale et limitée pour fournir des appareils compatibles DFT pour divers systèmes populaires (par exemple, les systèmes d'exploitation Linux et Windows x86 et x86-64).
![](https://img-blog.csdnimg.cn/20190504221401179.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5nMTQ5MTY=,size_16,color_FFFFFF,t_70)
instruments et l' analyse de la série de codes, et x région de marque sur un octet de tagmap de marque.
4. Implémentation
Nous avons utilisé la broche 2.9 pour implémenter libdft, qui est actuellement disponible pour les systèmes d'exploitation Linux et Windows. Notre prototype convient aux applications multi-processus et multithread non modifiées fonctionnant sur des processeurs x86 32 bits sous Linux, mais il peut être étendu sur l'architecture x86-64 et le système d'exploitation Windows avec un effort modéré (nous allons (La section 7 traite des futurs ports). Les principaux composants de libdft sont illustrés à la figure 3.
4.1 Tagmap
L'implémentation de tagmap joue un rôle vital dans les performances globales de libdft, car la logique DFT injectée s'exécute toujours sur le tag de données.
4.1.1 Étiquettes enregistrées
Nous stockons les étiquettes des 8 registres à usage général (GPR) de l'architecture x86 dans la structure vcpu, qui fait partie du mappage des étiquettes (voir la figure 3). Veuillez noter que nous marquons et suivons uniquement les registres qui peuvent être utilisés directement par l'application (tels que GPR). Le pointeur d'instructions (EIP), les EFLAGS et les registres de segments ne seront ni marqués ni suivis. Rappelons que, selon notre définition de la DFT, nous ne suivons que les dépendances de flux de données directes, il est donc sûr d'ignorer les EFLAGS. Cependant, les instructions exécutées conditionnellement sur la base du contenu d'EFLAGS sont traitées de manière appropriée (par exemple, CMOVcc, SETcc). De plus, pour plus de simplicité, les registres à virgule flottante (FPU) et les registres SSE (XMM, MMX) sont actuellement ignorés. Afin de prendre en charge ces registres à l'avenir, nous avons seulement besoin d'agrandir le vcpu.
Le tagmap contient plusieurs structures vcpu, une pour chaque thread d'exécution. Plus précisément, libdft capture les événements de création de threads et de terminaison de threads des applications, et gère dynamiquement le nombre de structures vcpu. Nous utilisons son ID virtuel attribué à chaque thread (c'est-à-dire une valeur incrémentielle de base zéro) pour trouver la structure appropriée pour chaque thread. Pour les balises de taille bit, nous utilisons un octet pour stocker les quatre balises 1 bit requises pour chaque GPR 32 bits, de sorte que la surcharge d'espace vcpu pour chaque thread est de 8 octets. De même, dans le cas de balises de taille octet, chaque GPR 32 bits nécessite 4 octets, de sorte que la surcharge d'espace du processeur virtuel devient 32 octets par thread.
4.1.2 Balises mémoire
Balises de taille binaire Lorsque libdft est configuré pour utiliser des balises de taille bit, il stocke les balises de mémoire dans une structure plane de taille fixe (voir le bitmap mem de la figure 3), qui correspond à chacune des mémoires adressables du processus Un octet est enregistré. La taille totale de l'espace d'adressage virtuel dans le système x86 est de 4 Go (232), mais le système d'exploitation se réserve une partie de cet espace pour lui-même (c'est-à-dire le noyau). La quantité d'espace réservé au noyau dépend du système d'exploitation. Linux utilise généralement un partitionnement de mémoire 3G / 1G, laissant 3 Go d'espace d'adressage pour le processus. Dans ce cas, nous devons réserver en permanence 384 Mo pour tagmap.
La balise mémoire de l'adresse vaddr peut être obtenue de la manière suivante: tval = mem_bitmap [vaddr >> 3] & (MASK << (vaddr & 0x7)). Plus précisément, nous utilisons les 29 bits les plus significatifs (MSB) de vaddr comme index d'octets dans le bitmap mem pour sélectionner l'octet contenant la marque vaddr. Ensuite, nous considérons les 3 bits les moins significatifs de vaddr comme le décalage de bit dans l'octet acquis précédemment, et en définissant MASK sur 0x1, nous obtenons le bit de marque d'un seul octet. De même, si MASK est 0x3 ou 0xF, nous obtenons respectivement le bit de marque d'un mot ou d'un double mot. La surcharge d'espace d'adressage imposée par le bitmap mem est de 12,5%. L'utilisation d'une structure de taille fixe au lieu d'une structure gérée dynamiquement (par exemple, une table de type page) nous permet d'éviter les pénalités liées à sa gestion et à son accès. Veuillez noter que bien que la taille du tagmap soit raisonnable sur les systèmes 32 bits, dans les architectures 64 bits, les bitmaps plats ne sont pas pratiques. Par exemple, en x86-64, le bitmap avion nécessite 32 To.
Balises de taille octet Lorsque libdft utilise des balises de taille octet, il les stocke dans des segments de mappage de balise alloués dynamiquement (voir tseg sur la figure 3). Chaque fois qu'une application acquiert implicitement un nouveau bloc de mémoire en effectuant un chargement d'image (par exemple, lors du chargement d'un objet partagé dynamique), ou en acquérant explicitement de la mémoire via des appels système tels que mmap, mremap, brk et shmat, libdft intercepte l'événement et Allouez une zone de mémoire continue de taille égale. Par exemple, si une application utilise mmap pour demander une carte anonyme de 1 Mo, libdft utilisera le segment de tagmap 1 Mo pour «masquer» la zone allouée pour stocker les marqueurs d'octets dans la mémoire mmap-ed. Plus important encore, le segment de mappage de marqueurs correspondant au bloc de mémoire partagée est également partagé. Par conséquent, deux processus exécutés sous libdft peuvent partager efficacement la mémoire fantôme. À notre connaissance, nous sommes les premiers à mettre en œuvre ce schéma de partage d'étiquettes.
Nous utilisons le système de pseudo-fichiers proc (/ proc / <pid> / maps) pour obtenir des informations sur la zone mémoire mappée pendant le chargement ou avant que libdft soit attaché à l'application. De cette façon, nous pouvons obtenir l'emplacement de la pile et d'autres objets de mémoire mappés par le noyau, tels que les pages vDSO et vsyscall, et allouer le segment de tagmap correspondant en conséquence. Afin de faire face à l'expansion implicite de la pile, libdft pré-alloue un segment de tagmap pour couvrir la pile comme si elle était étendue à sa valeur maximale, qui peut être obtenue par getrlimit (RLIMIT STACK). Cependant, les piles de threads n'ont pas besoin d'être les mêmes car elles sont explicitement allouées à l'aide de mmap.