8
votes

Vecteur.Resize fonction de la mémoire de corruption lorsque la taille est trop grande

Qu'est-ce qui se passe est que je lis je lis des paquets de cryptage et je rencontre un paquet corrompu qui redonne un très grand nombre aléatoire pour la longueur.

#include "stdafx.h"
#include <vector>
#include <iostream>
#include <math.h>
#include <typeinfo>

typedef std::vector<unsigned char> vector_unsigned_char;

void fill(vector_unsigned_char& v) {
    for (int i=0; i<100; i++) v.push_back(i);
}


void oput(vector_unsigned_char& v) {
    std::cout << "size: " << v.size() << std::endl;
    std::cout << "capacity: " << v.capacity() << std::endl;
    std::cout << "max_size: " << v.max_size() << std::endl << std::endl;
}

void main(int argc, char* argv[]) {
    {
        vector_unsigned_char v;

        fill(v);

        try{
            v.resize(static_cast<size_t>(3555555555));
        }catch(std::bad_alloc&) {
            std::cout << "caught bad alloc exception" << std::endl;
        }catch(const std::exception& x) {
            std::cerr << typeid(x).name() << std::endl;
        }catch(...) {
            std::cerr << "unknown exception" << std::endl;
        }

        oput(v);    
        v.reserve(500);
        oput(v);
        v.resize(500);
        oput(v);
    }

    std::cout << "done" << std::endl;
}


3 commentaires

Je suis curieux de savoir quelle version compilateur / stl utilisez-vous. Les implémentations que j'ai accès ne corrompre pas l'objet vectoriel si l'allocation a échoué.


@cchampion: "Sur ma machine de développement VS6 ..." Vous l'avez dit plus tôt, nous n'aurions pas perdu autant de temps à ce sujet. C'est plus de 10 ans de technologie! Bien sûr, c'est buggy. Voir ma réponse.


Consultez mes modifications plus récentes - Le problème est un bug dans le (mais pas celui qui a été discuté sur la page Dunkumway VC Bugs).


3 Réponses :


7
votes

Je pense que vecteur :: max_size () est toujours toujours une chose «codée dur» - il est indépendant de la quantité de mémoire que le système / la bibliothèque est préparé pour allouer de manière dynamique. Votre problème semble être un bogue dans la mise en œuvre du vecteur qui corrompt les choses lorsqu'une attribution échoue.

'Bug' pourrait être trop fort d'un mot. Vecteur :: redimension () est défini en termes de Vector :: Insérer () et la norme indique ceci sur vecteur :: insert () :

Si une exception est lancée autrement que par le constructeur de copie ou l'opérateur d'affectation de T, il n'y a aucun effet

Il semble donc qu'il peut y avoir des moments où le fonctionnement () est autorisé à corrompre un vecteur, mais il serait toujours sympa si l'opération était en sécurité (et je pense que cela ne serait pas T Soyez hors de ligne pour vous attendre à ce que la bibliothèque fasse cela, mais peut-être qu'il est plus difficile que j'imagine).

Vous semblez avoir quelques options raisonnables:

  • Modifier ou mettre à jour vers une bibliothèque qui n'a pas le bogue de corruption (quelle version compilateur / bibliothèque utilisez-vous?)
  • au lieu de vérifier contre vector :: max_size () définir nmaxsize à votre propre maximum raisonnable et faites ce que vous avez ci-dessus mais en utilisant ce seuil à la place.

    EDIT:

    Je vois que vous utilisez VC6 - il y a définitivement un bug dans vecteur :: redize () qui pourrait avoir quelque chose à voir avec votre problème, mais en regardant le patch, je ne fais pas Voyez comment (en fait c'est un bogue dans vecteur :: insérer () , mais comme mentionné, redimensionne () appels insérer () ). J'imaginerais qu'il serait intéressant de visiter page de dinkumwares pour les corrections de bugs à VC6 et appliquer le Corrections.

    Le problème peut également avoir quelque chose à voir avec le correctif sur cette page - on ne sait pas ce que le bogue est discuté là-bas, mais vecteur :: insérer () Est-ce que Call _Destroy () et vecteur <> définit le nom _ty afin que vous puissiez suivre ce problème. Une bonne chose - vous n'aurez pas à vous soucier de gérer les modifications apportées aux en-têtes, car Microsoft ne les touche jamais. Assurez-vous simplement que les patchs en font le contrôle de la version et sont documentés.

    Notez que Scott Meyers dans "Efficace STL" suggère d'utiliser SGI's ou < Un href = "http://www.stlport.org/" rel = "noreferrer"> la bibliothèque de STLORT pour obtenir un meilleur support STL que par VC6. Je n'ai pas fait ça, donc je ne suis pas sûr à quel point ces bibliothèques fonctionnent (mais je n'ai pas aussi utilisé VC6 avec beaucoup de stl). Bien sûr, si vous avez la possibilité de passer à une version plus récente de VC, faites-le.


    une autre modification:

    Merci pour le programme de test ...

    _Alloquer () implémentation pour l'allocator par défaut (dans ) utilise un Int signé pour spécifier le nombre d'éléments à allouer et si la taille est passée. In est négatif (ce qui apparaît apparemment ce que vous faites - certainement dans le programme de test, vous êtes), le _Allocate () fonctionne la taille de l'allocation demandée à zéro et au produit. Notez qu'une demande d'allocation de taille zéro va toujours toujours réussir (pas que vecteur vérifie quand même une panne), donc le vecteur vecteur :: redize () fonction joyeusement essaie de Déplacez son contenu dans le nouveau bloc, ce qui n'est pas assez grand pour le moins important. Donc, le tas est corrompu, il va probablement toucher une page de mémoire invalide, et quel que soit votre programme.

    de sorte que la fin de la ligne ne soit jamais demandée à VC6 d'allouer plus de int_max d'objets en une fois. Probablement pas une bonne idée dans la plupart des circonstances (VC6 ou autre).

    En outre, vous devez garder à l'esprit que VC6 utilise un idiome pré-standard de retour 0 à partir de Nouveau lorsqu'une allocation échoue plutôt que de lancer Bad_alloc . .


6 commentaires

"Si une exception est lancée autrement que par le constructeur de copie ou l'opérateur d'affectation de T, il n'y a pas d'effets" IRTA ", c'est comme si redimensionne () n'a pas été appelé." Et je suis assez sûr que aucune opération sur un vecteur n'est censée corrompue la mémoire.


L'opération Insérer () peut entraîner des opérations de copie / d'affectation (lorsque le contenu du vecteur est copié dans une nouvelle allocation) - ceux-ci sont autorisés à «avoir un effet». Cela ne devrait rien faire aussi mal que de corrompre le tas par exemple, mais il n'est pas clair si c'est ce qui se passe à l'OP. Une exception sous ces conditions est autorisée à donner un vecteur changé (peut-être que tous les éléments du vecteur ne se rendent pas au nouveau). Son code peut constater que le vecteur n'a plus de sens. De toute façon, n'est pas un grand comportement et je conviens qu'une implémentation stl pourrait mieux gérer la situation.


Vous avez raison, cependant que la copie / l'attribution d'éléments dans un vecteur ne devrait aboutir à aucune exception - qui me semble signaler à une mise en œuvre de Buggy STL qui ne gère pas la sortie de la mémoire bien. Je serais intéressé par des détails sur la plate-forme / compilateur / bibliothèque utilisée.


"Une exception dans ces conditions est autorisée à donner lieu à un vecteur changé" si tel est vrai, je serais surpris. En outre, je ne vois pas comment lire ceci de ce que vous avez cité.


La façon dont je lis cette ligne dans la norme est que si une exception est lancée dans un Vecteur :: insert () appel alors que rien ne sera arrivé au vecteur ("pas d'effets") à moins que l'exception ne soit arrivée À partir d'une copie CTOR ou d'un opérateur d'affectation, auquel cas on pourrait avoir eu un effet (non spécifié). Là encore, les documents de normalisation ne sont pas connus pour la clarté de cristal. Je pourrais donc être hors de base.


@Michael: À la lecture de la ré-lecture, je suis d'accord avec vos doutes.



5
votes

Je suggère fortement que vous vérifiez vos données pour les corruptions avant d'appeler des fonctions de bibliothèque avec des arguments erronés!

Utilisez une sorte de code de hachage ou cochez la somme algorithme de somme sur vos paquets. Vous ne pouvez pas compter sur la bibliothèque pour vous aider puisqu'il ne peut pas faire: Il se peut que vous lui donniez un corrompu mais toujours valide (du point de vue de la bibliothèque) la taille réelle qui est réelle afin d'allouer par exemple 768 Mo de RAM. Cela peut fonctionner s'il y a suffisamment de mémoire libre dans le système mais peut échouer s'il existe d'autres programmes qui consomment trop de mémoire sur votre machine de 1024 Mo.

Ainsi, comme indiqué ci-dessus: Vérifiez d'abord!


2 commentaires

Je suis d'accord. Je pense que la racine de votre problème est que vous comptez sur votre algorithme de cryptage pour vous indiquer la taille "prétend" être. Vous avez vraiment besoin d'une application de taille de bloc avec un rembourrage (comme MD5 le fait) ou d'avoir un autre moyen hors bande de fournir des informations sur la taille.


Je suis d'accord, nous avons besoin d'une méthode pour valider les paquets. Je vais mentionner cela aux autres programmeurs de ce projet lundi. Pour l'instant, je vais juste sauter le paquet s'il est plus grand que 5 Mo.



4
votes

Je n'ai aucune idée de ce que vous voulez dire lorsque vous dites "Redimentation a une mémoire corrompue". Comment déterminez-vous cela?

FWIW, je ne suis pas d'accord avec La réponse de Michael . Si std :: vecteur <> :: redimensionnement () jette sur l'expansion de vecteur, je vois deux possibilités:

  1. L'un des constructeurs utilisés pour remplir le nouvel espace (ou copier les éléments) jeté ou
  2. l'allocateur utilisé pour cultiver le vecteur de
  3. ou le vecteur déterminé à portée de main que la taille demandée est trop nombreuse et jette.

    avec std :: vecteur Nous pouvons rejeter en toute sécurité # 1, de sorte que cela part # 2. Si vous n'utilisez aucun allocator spécial, std :: allocator doit être utilisé et, Afaik, qui appellera nouveau pour allouer la mémoire. Et nouveau lancerait std :: bad_alloc . Cependant, vous dites que vous ne pouvez pas attraper cela, alors je ne sais pas ce qui se passe.

    Peu importe ce qu'il est, il devrait être dérivé de std :: exception , afin que vous puissiez le faire pour savoir: xxx < P> Quel est le résultat de cela?

    Quoi qu'il en soit, quoi que ce soit, je suis assez sûr de ne pas corrompre la mémoire. Soit c'est un bogue dans votre mise en œuvre STD lib liber (improbable, si vous me demandez, sauf si vous utilisez un très ancien) ou que vous avez fait quelque chose de mal ailleurs.


    edit Maintenant que vous avez dit que vous utilisez VS6 ...

    Vous auriez dû dire cela plus tôt. VC6 a été publié il y a plus d'une décennie, après que MS avait perdu leur vote dans le comité STD, car ils n'avaient pas comparu à des réunions depuis trop longtemps. La mise en œuvre de STD LIB qu'ils avaient expédiées provenaient de Dinkumware (Good), mais en raison du problème juridique, c'était celui pour VC5 (très mauvais), qui avait beaucoup de bugs plus petits et plus importants et n'avait même pas de soutien aux modèles membres, même si Le compilateur VC6 a soutenu. Honnêtement, qu'attendez-vous d'un tel produit?

    Si vous ne pouvez pas passer à une version VC décente (je recommanderais au moins VC7.1 aka vs.net 2003, comme c'était celui qui a fait le saut majeur vers la conformité standard), au moins voir si Dinkumware Vendez toujours une version VC6T de leur excellente bibliothèque. (En fait, je serais surpris, mais ils avaient l'habitude d'en avoir un et vous ne savez jamais ...)

    comme pour les exceptions: dans la version VC antérieure (ceci inclut VC6 et n'inclut pas VC8 AKA VS .NET 2005, je ne suis pas sûr de VC7.1, cependant) par défaut des violations d'accès pourraient être capturées par Catch (...) . Donc, si un tel bloc de captures a attrapé quelque chose, vous ne sauriez pas si cela était une exception C ++. Mon conseil serait de ne utiliser que attraper (...) dans la conférence avec lancer; afin de laisser cette exception passer. Si vous le faites, vous obtenez un véritable accident chez AV et êtes capable de les tracer dans le débogueur. Si vous ne le faites pas, AV sera avalé et vous êtes coincé avec une application qui est partie Berserk sans que vous sachiez même. Mais faire quelque chose mais abandonner avec une application AV'ed n'a aucun sens. Un AV est un résultat de comportement non défini et après cela, tous les paris sont éteints.


4 commentaires

La raison pour laquelle je dis que cela corrompue la mémoire est parce que des déclarations de journal simples commencent à lancer des exceptions et que tout un tas d'affirmations liées à la mémoire continue de se lever. Après la fonction de redimensionnement, tout le programme perd l'esprit. Cela n'arrive pas si vous réussissez à redimensionner une longueur raisonnable. Cela n'arrive pas si je saute ce paquet non plus. Je vais essayer ce code pour déterminer le type d'exception, je ne savais pas sur Typeid! Merci.


Si vous ne saviez pas: vous devez #include pour cela.


Croyez-le ou non, le const Std :: Exception et Handler ne l'ont pas attrapé. Je n'indique aucune idée de cette exception. Maintenant, je commence vraiment à croire que la version de la STL que nous utilisons a un bogue dedans. Je discuterai de cela avec le programmeur mène lundi.


Pour attraper avec succès une violation d'accès (AKA SEH Exception), vous devez définir le réglage du compilateur / EHA. @cchampion: Cela expliquerait pourquoi vous n'attribuez pas l'exception, car ce n'est probablement pas une exception C ++