J'ai besoin d'une fonction qui (comme SecureZeroMemory de WinAPI) remet toujours à zéro la mémoire et ne soit pas optimisée, même si le compilateur pense que la mémoire ne sera plus jamais accessible après cela. Semble être un candidat parfait pour volatile. Mais j'ai des problèmes pour que cela fonctionne avec GCC. Voici un exemple de fonction:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Assez simple. Mais le code que GCC génère réellement si vous l'appelez varie énormément avec la version du compilateur et la quantité d'octets que vous essayez de mettre à zéro. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 et 4.5.3 n'ignorent jamais le volatile.
- GCC 4.6.4 et 4.7.3 ignorent volatile pour les tailles de tableau 1, 2 et 4.
- GCC 4.8.1 jusqu'à 4.9.2 ignore volatile pour les tailles de tableau 1 et 2.
- GCC 5.1 jusqu'à 5.3 ignore volatile pour les tailles de tableau 1, 2, 4, 8.
- GCC 6.1 l'ignore simplement pour n'importe quelle taille de tableau (points bonus pour la cohérence).
Tout autre compilateur que j'ai testé (clang, icc, vc) génère les magasins attendus, avec n'importe quelle version de compilateur et n'importe quelle taille de tableau. Donc, à ce stade, je me demande, est-ce un bogue du compilateur GCC (assez ancien et grave?), Ou est-ce que la définition de volatile dans la norme indique imprécise qu'il s'agit en fait d'un comportement conforme, ce qui rend pratiquement impossible d'écrire un portable " Fonction SecureZeroMemory "?
Edit: Quelques observations intéressantes.
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <atomic>
void callMeMaybe(char* buf);
void volatileZeroMemory(volatile void* ptr, std::size_t size)
{
for (auto bytePtr = static_cast<volatile std::uint8_t*>(ptr); size-- > 0; )
{
*bytePtr++ = 0;
}
//std::atomic_thread_fence(std::memory_order_release);
}
std::size_t foo()
{
char arr[8];
callMeMaybe(arr);
volatileZeroMemory(arr, sizeof arr);
return sizeof arr;
}
L'écriture possible depuis callMeMaybe () fera que toutes les versions de GCC sauf la 6.1 génèrent les magasins attendus. Commenter dans la clôture de la mémoire fera également générer les magasins par GCC 6.1, bien qu'en combinaison avec l'écriture possible de callMeMaybe ().
Quelqu'un a également suggéré de vider les caches. Microsoft n'essaye pas du tout de vider le cache dans "SecureZeroMemory". Le cache va probablement être invalidé assez rapidement de toute façon, donc ce n'est probablement pas un gros problème. De plus, si un autre programme essayait de sonder les données, ou s'il allait être écrit dans le fichier d'échange, ce serait toujours la version remise à zéro.
Il y a aussi quelques inquiétudes à propos de GCC 6.1 utilisant memset () dans la fonction autonome. Le compilateur GCC 6.1 sur godbolt pourrait être une version cassée, car GCC 6.1 semble générer une boucle normale (comme 5.3 le fait sur godbolt) pour la fonction autonome pour certaines personnes. (Lisez les commentaires de la réponse de zwol.)
volatile
est un bogue, sauf preuve du contraire. Mais très probablement un bug.volatile
est tellement sous-spécifié qu'il est dangereux - ne l'utilisez pas.