Trap tous les access à une plage d’adresses (Linux)

Contexte

J’écris un cadre pour permettre la co-simulation de la RTL s’exécutant dans un simulateur et un logiciel hôte non modifié. Le logiciel hôte est conçu pour contrôler le matériel réel et fonctionne généralement de deux manières:

  1. Lecture / écriture des appels via un pilote
  2. Accès mappé à la mémoire à l’aide de mmap

Le premier cas est assez simple: écrivez une bibliothèque qui implémente les mêmes appels en lecture / écriture que le pilote et établissez un lien avec celle-ci lors de l’exécution d’une simulation. Tout fonctionne à merveille et je peux utiliser un logiciel de production non modifié pour stimuler mes simulations RTL.

Le deuxième cas s’avère beaucoup plus difficile que le premier …

Piège mmap

Au départ, je pensais pouvoir utiliser LD_PRELOAD pour intercepter l’appel mmap. Dans mon implémentation de mmap une mémoire alignée sur la page, puis la mprotect et définirais un gestionnaire de signal pour qu’il SIGSEGV .

Cette approche pose de nombreux problèmes:

Lire vs écrire

Je peux déterminer l’adresse de l’access à partir de siginfo_t->si_addr mais pas si l’access était en lecture ou en écriture.

Attraper des access répétés

Dans le gestionnaire de signal, je dois protéger la région de la mémoire, sinon le SIGSEGV sera répété dès que mon gestionnaire sera fermé et que le code hôte ne pourra plus continuer. Cependant, si je déprotège la région, mon gestionnaire de signal ne bloquera pas les access ultérieurs.

Manipulation du signal

Bloquer un gestionnaire de signal alors que le simulateur pilote la RTL et renvoie un résultat enfreint toutes sortes de règles de programmation – étant donné que le simulateur peut déclencher toutes sortes d’autres événements et exécuter du code arbitraire avant de renvoyer un résultat de cet access.

Autres approches

Je me demandais s’il était possible de créer un object de type fichier qui se comporte comme un disque plutôt que d’utiliser mprotect sur un tampon. Je n’ai trouvé aucune information suggérant que cela est faisable.

Des questions

Est-il possible de piéger tous les access dans une région mmap et comment?

  • Les access doivent être bloqués pour une durée indéterminée (pendant l’exécution du simulateur)
  • Les access en lecture doivent récupérer une nouvelle valeur placée par mon piège

En supposant que LD_PRELOAD et mprotect est le meilleur itinéraire:

  • Puis-je déterminer si l’access était un appel en lecture ou en écriture?
  • Comment puis-je piéger les access ultérieurs car je dois mprotect la mprotect la région?

questions connexes

Comment écrire un gestionnaire de signal pour prendre SIGSEGV?

Possibilité de piéger en écriture sur l’adresse (x86 – linux)

Sur X86, vous pouvez définir l’indicateur Trap pour le contexte de l’appelant pour obtenir SIGTRAP après une instruction (cet indicateur est généralement utilisé pour un pas simple). Autrement dit, lorsque SIGSEGV est rencontré, vous définissez TF dans les EFLAGS de l’appelant (voir ucontext.h ), activez la lecture avec mprotect et retournez. Si SIGSEGV est répété instantanément avec la même adresse IP, vous activez l’écriture (et désactivez éventuellement la lecture, si vous souhaitez distinguer l’access en lecture-modification-écriture de l’access en écriture seule). Si vous obtenez SIGSEGV à partir de la même IP pour une protection en lecture seule et en écriture seule, activez la lecture-écriture.

Chaque fois que vous obtenez SIGTRAP, vous pouvez parsingr quelle valeur a été écrite (s’il s’agissait d’un access en écriture), et vous pouvez également protéger de nouveau la page pour intercepter les futurs access.

Correction: si les lectures et les écritures peuvent avoir des effets secondaires, essayez d’abord la protection en écriture seule, puis appliquez les effets secondaires de lecture et essayez la protection en lecture seule, puis activez l’écriture et gérez les effets secondaires du gestionnaire SIGTRAP final.

MISE À JOUR: Je me suis trompé en recommandant une protection hypothétique en écriture seule qui ne semble pas exister sur la plupart des architectures. Heureusement, il existe un moyen plus simple de savoir si l’opération qui a échoué tente de lire la mémoire, au moins sur x86:

L’exception d’erreur de page envoie un code d’erreur à la stack, qui est disponible dans le gestionnaire SIGSEGV de Linux en tant que membre err de la structure sigcontext . Le bit 1 du code d’erreur est 1 pour les erreurs d’ écriture et 0 sinon. Pour une opération read-modify-write, il sera initialement 0 (vous pouvez ici émuler la lecture, sachant exactement que cela va se produire).