4
votes

Puis-je redimensionner un vecteur qui a été déplacé?

J'ai un code dans lequel je veux m'assurer absolument qu'un std :: vector déplacé ne laissera pas de données secrètes (pensez à la gestion des clés cryptographiques). Dans le constructeur de déplacement de ma classe, je fais quelque chose comme:

X(X&& rhs): secret_vector{std::move(rhs.secret_vector)}{
    rhs.secret_vector.resize(N);
    safe_zero(rhs.secret_vector); // zero out all elements
    rhs.secret_vector.resize(0);
}

Comme vous pouvez le voir, je réutilise le vecteur secret après m'en être éloigné. J'ai regardé

Réutiliser un conteneur déplacé?

mais ce n'était pas le cas il est absolument clair que je peux le faire (je n'ai pas compris ce que sont réellement les «pré-conditions»).

Ma question est la suivante: Puis-je redimensionner un std :: vector déplacé, effectuer une opération dessus, puis le redimensionner à zéro?

p >


23 commentaires

std :: move ne déplace pas le contenu du std :: vector. Donc, si vous voulez mettre à zéro le contenu du vecteur, vous pouvez simplement safe_zero it , pas besoin de mettre à zéro secret_vector après avoir été déplacé.


@YoYoYonnY Je sais que std :: move ne bouge rien. Cependant, si quelqu'un fait quelque chose comme X x = std :: move (y); , alors y est dans un état déplacé. Je veux m'assurer que toutes les données contenues dans y sont mises à zéro. La norme NE GARANTIT PAS que le vecteur y transfère simplement le pointeur interne, en fait un ( mais stupide) déménagement sera de simplement copier les données.


Avec la garantie de complexité du constructeur de mouvement de vecteur, je pense que le vecteur déplacé ne peut plus avoir d'anciennes données. (l'attribution de déplacement pourrait être plus délicate car swap serait une implémentation valide).


@ Jarod42 C'était mon initiale cependant ... mais je ne trouve pas de réponse / citation définitive disant cela. Et je ne veux vraiment pas laisser flotter des données sensibles, quoi qu'il arrive. Avez-vous une référence standard dans laquelle std :: vector (std :: vector &&) est garanti O (1) ?


@vsoftco Selon cppreference , c'est faux. Le standard garantit que le vecteur y transfère son pointeur interne.


cppreference indique un O (1) complexité pour (6) vecteur (vecteur && autre) / * noexcept * / .


@YoYoYonnY, Jarod42, merci! J'ai raté ça d'une manière ou d'une autre ...


Le vecteur interne est transféré mais il est également possible que le déplacement se fasse en échange les données du vecteur donc dans un constructeur tout va bien mais dans une affectation de déplacement vous devez remettre à zéro les données déplacées à partir de (ou les données déplacées avant le déplacement).


Je garderai probablement toujours mon implémentation "paranoïaque", car je ne veux pas avoir de "DEBUG" comme build dans lequel des copies folles peuvent se produire ou des trucs de ce genre ...


@Galik Droite ...


Il est possible que le compilateur optimise la remise à zéro s'il vous voit écrire dans temporaire cependant ... donc il vaut probablement la peine d'utiliser une fonction opaque pour faire l'effacement, donc le compilateur ne le fait pas maintenant s'il y a des effets secondaires ou non.


@Galik Oui, c'est ce que j'utilise derrière les rideaux. En particulier, j'appelle une fonction de type C à partir d'une bibliothèque déjà compilée. Le compilateur ne doit pas être en mesure de déduire ce que fait la fonction au moment de la compilation.


Le redimensionnement à zéro sera probablement un no-op puisque la taille sera probablement déjà zéro. Ainsi, ni la logique d'expansion ni de contraction du redimensionnement ne fonctionnera et rien ne se passera.


Copier le vecteur est plus logique que de le déplacer et de repousser la source


@DavidSchwartz Je redimensionne d'abord en N , puis je redimensionne à 0. Oh .. vous voulez dire que le compilateur peut simplement décider d'ignorer mon premier redimensionner (N) ?! Ce sera mauvais!


Le redimensionnement allouera probablement un bloc de mémoire entièrement nouveau et n'aura aucun effet sur le bloc existant qui contient les données. Si les données y sont copiées, vous écrasez simplement la copie nouvellement créée. Si ce n'est pas le cas, vous écrasez les données vides. Dans aucun des cas, le redimensionner ne vous aide.


@ M.M Vous voulez dire simplement supprimer - utiliser la sémantique de déplacement? Cela peut en effet être une option si les choses ont tendance à devenir moche ...


@DavidSchwartz La taille initiale du vecteur déplacé est 0 . Je ne suis donc pas préoccupé par le redimensionner (N) . Cela allouera simplement de la mémoire et rien ne sera copié. Est-ce que j'ai râté quelque chose? Rappelez-vous que tout d'abord le vecteur est déplacé, et cela garantira que la taille du vecteur déplacé est zéro. Je veux juste m'assurer que, quoi qu'il arrive, les données ne «restent» pas dans ce qui est déplacé. Paranoïa cryptographique que vous connaissez probablement très bien ...


@vsoftco Alors pourquoi ne pas copier l'ancien vecteur dans un nouveau vecteur, écraser les données de l'ancien vecteur, puis le redimensionner à zéro?


@DavidSchwartz C'est ce que je fais en fait, mais au lieu de copier, j'utilise le déplacement. Je crains juste que quelqu'un écrive quelque chose comme: Signature s = Signature (some_secret_key) . Dans ce cas, le rhs est une rvalue, s appellera le constructeur de déplacement, et déplacera les rhs dans s . Cependant, je veux m'assurer que la mémoire "chaude" dans ce qui était rhs n'est plus chaude. Je peux simplement désactiver complètement la sémantique de déplacement, mais dans un souci d'apprentissage, je voulais voir si ma solution est valide. Notez que dans le cas de la copie, je suis en sécurité, car le destructeur se charge de remettre à zéro la mémoire.


@vsoftco Je suggère que vous implémentiez vos propres opérations de déménagement. Il n'y a aucun moyen de savoir avec certitude ce que le déplacement d'un std :: vector fera. Il pourrait en faire une copie inaccessible. (Ce n'est probablement pas le cas, mais vous ne pouvez pas le savoir.)


@DavidSchwartz bon point, je vais y réfléchir


Vous devriez envisager d'utiliser volatile


3 Réponses :


4
votes

[defns.valid] état valide mais non spécifié valeur d'un objet qui n'est pas spécifiée sauf que les invariants de l'objet sont satisfaits et que les opérations sur le l'objet se comporte comme spécifié pour son type

[Exemple: Si un objet x de type std :: vector est dans un état valide mais non spécifié, x.empty () peut être appelé inconditionnellement, et x.front () ne peut être appelé que si x.empty () renvoie false. - fin d'exemple]

std :: vector :: resize n'a pas de conditions préalables. Quel que soit l'état valide d'un vecteur, le redimensionnement n'aurait pas de comportement indéfini (sans tenir compte de l'UB causé par les constructeurs d'éléments contenus; mais ceux-ci ne sont pas appelés lorsque l'argument est 0).


0 commentaires

4
votes

Ma question est la suivante: puis-je redimensionner un std :: vector déplacé, effectuer une opération dessus, puis le redimensionner à zéro?

Un objet déplacé doit être dans un état non spécifié mais valide.

Vous avez donc le droit de le redimensionner (aucune condition préalable requise pour cela). safe_zero et effacez-le.

(je n'ai pas compris ce que sont réellement les "pré-conditions").

Il y a des conditions d'état que l'objet devrait avoir pour ne pas appeler UB.

Par exemple, operator [] (std :: size_t i) requiert que i .

resize () , clear () n'a pas d'exigences.


0 commentaires

2
votes

Oui. L'objet est dans un état valide mais non spécifié, comme indiqué dans la question liée. Cela signifie que vous ne pouvez rien supposer sur le contenu de std :: vector . L'appel de size est sûr, mais il peut ne pas renvoyer la même valeur qu'avant le déplacement. L'état du vecteur est valide, ce qui signifie qu'il n'y a pas de pointeurs suspendus à l'intérieur ou quoi que ce soit et toutes les fonctions membres, y compris resize fonctionneront très bien. De plus, l'appel de resize est l'une des rares fonctions significatives à appeler car elle "respécifie" l'état du vecteur.


0 commentaires