2010-05-28 4 views
1

J'ai beaucoup lu, y compris here on SO qui suggère que c'est une très mauvaise idée en général et que la seule chose que vous pouvez faire en toute sécurité est de quitter le programme. Je ne suis pas sûr que ce soit vrai.Est-il possible d'intercepter une violation d'accès dans ce scénario?

Ceci est pour un allocateur de mise en commun de mémoire qui remet des allocations importantes à malloc. Pendant pool_free() un pointeur doit être vérifié s'il appartient à un pool ou a été alloué avec malloc. En arrondissant l'adresse à la limite de 1 Mo la plus proche, j'obtiens un pointeur vers le début d'un bloc de mémoire dans le pool, ou undefined si malloc a été utilisé. Dans le premier cas je peux facilement vérifier que le bloc de mémoire appartient au pool, mais si ce n'est pas le cas je vais échouer cette vérification, OU Je vais avoir une violation d'accès (notez que ceci est en lecture seule processus). Ne pourrais-je pas attraper ceci avec SEH (Windows) ou manipuler le signal (POSIX) et le traiter simplement comme une vérification échouée? (c'est-à-dire que cela n'est possible que si malloc a été utilisé, donc passer le ptr à free())

Editer: Les gens semblent manquer le bloc opératoire ci-dessus. Je ne m'attends pas à obtenir une violation d'accès si le pointeur a été alloué avec malloc, mais c'est un résultat possible. La procédure utilisant le pointeur au début du bloc (à la limite de 1 Mo) est de vérifier un nombre magique, puis de suivre un pointeur vers le pool de mémoire, et de vérifier qu'il contient effectivement le pointeur mentionné ci-dessus. Si l'une de ces étapes en lecture seule produit une violation d'accès, elle échoue à la validation aussi sûrement que si une étape individuelle échouait.

+1

Tout comportement avaidable _undefined_ devrait être _really_ être corrigé. Tout le reste est de la folie. – LukeN

Répondre

1

Il n'est pas nécessaire de mettre en œuvre un mécanisme réactif. Vous pouvez obtenir en face du problème en alignant les allocations de segments à une limite de 1 Mo:

  1. Fenêtres: _aligned_malloc(size, 1<<20)
  2. Unix: memalign(1<<20, size)

En utilisant cette approche, arrondi vers le bas à 1 Mo est garanti pour pointer dans un bloc de mémoire alloué, et vous devez simplement discerner si cette adresse est dans le pool ou à l'extérieur (dans ce cas, il était évidemment malloc ed).

Vous devez faire attention à n'utiliser que l'allocation de tas alignée pour les objets réellement volumineux. Si vous l'utilisez pour, disons, une taille> 100 Ko, l'allocateur laissera d'énormes espaces entre les objets. Idéalement, utilisez-le uniquement pour les objets qui ne rentrent pas dans un bloc de pool de 1 Mo.

+0

Les gros objets dans mon cas ne sont pas si gros, donc ce ne sera pas une bonne idée. – Eloff

+0

Si les objets du pool sont beaucoup plus petits que les blocs du pool, vous pouvez arrondir à une limite plus fine proche du plus grand objet que vous voulez placer dans le pool et placer des nombres magiques dans le bloc, à ces limites . Vous utiliserez alors la même granularité plus petite lors de l'appel de malloc aligné. Il serait évidemment plus simple d'utiliser des blocs de piscine plus petits. –

+0

Oui, passer à 64k blocs de la piscine rendrait ce joli faisable. Utilisez VirtualAlloc/VirtualFree sur windows, qui garantissent que la mémoire est alignée sur une adresse 64k ou memalign sur unix ce qui laissera de petits espaces, mais fonctionne toujours. Pas de comportement indéfini. J'aime ça. – Eloff

2

Vous avez besoin d'un meilleur test. Il n'y a aucune garantie réelle que vous obtiendrez un AV si malloc a été utilisé car le point d'arrondi peut avoir été également alloué à votre application et ainsi votre accès à cette mémoire sera autorisé.

+0

ce n'est pas le test. Je m'attends à ce que l'accès soit autorisé la plupart du temps. – Eloff

0

Je pense que cela devrait être sûr. Mais probablement une mauvaise idée. Cependant, si vous avez besoin de mélanger de la mémoire allouée au pool avec de la mémoire non allouée au pool, je pense que vous devez stocker des informations à ce sujet quelque part. Peut-être que chaque allocation de mémoire faite en utilisant pool_alloc() pourrait avoir un en-tête minuscule, "caché" avant l'allocation réelle. Cet en-tête pourrait contenir des informations sur la façon dont il a été alloué. Donc char * block = (char *) pool_alloc (32) allouerait réellement 32 + sizeof (BlockHeader) octets. Et block-sizeof (BlockHeader) fournirait l'accès à l'en-tête.

IsBadReadPtr est obsolète selon msdn:

Important Cette fonction est obsolète et ne doit pas être utilisé.Malgré son nom, il ne garantit pas que le pointeur est valide ou que la mémoire pointée vers est sûr à utiliser. Pour plus d'informations, voir les remarques sur cette page.

0

Ok, inazaruk a posté une réponse qui a été fortement downvoted puis supprimé suggérant l'utilisation de IsBadReadPtr + VirtualQuery (pour éviter la garde ou pas de pages d'accès.) La lecture des liens qu'il m'a posté alertés sur le fait que la lecture d'un aléatoire zone de mémoire a des effets secondaires potentiels plus graves qu'une violation d'accès.

L'accès accidentel à une page de garde à la fin d'une pile de threads entraînera l'arrêt brutal du programme si cette pile de threads se développe.

Par conséquent, l'interception de la violation d'accès est potentiellement dangereuse. Cela répond à la question.

Questions connexes