Eu preciso de uma função que (como SecureZeroMemory do WinAPI) sempre zera a memória e não seja otimizada, mesmo se o compilador achar que a memória nunca mais será acessada depois disso. Parece um candidato perfeito para volátil. Mas estou tendo alguns problemas para realmente fazer isso funcionar com o GCC. Aqui está um exemplo de função:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Simples o suficiente. Mas o código que o GCC realmente gera se você chamá-lo varia muito com a versão do compilador e a quantidade de bytes que você está tentando zerar. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 e 4.5.3 nunca ignoram o volátil.
- GCC 4.6.4 e 4.7.3 ignoram voláteis para tamanhos de array 1, 2 e 4
- GCC 4.8.1 até 4.9.2 ignora volátil para tamanhos de array 1 e 2.
- GCC 5.1 até 5.3 ignora volátil para tamanhos de matriz 1, 2, 4, 8.
- O GCC 6.1 simplesmente o ignora para qualquer tamanho de array (pontos de bônus por consistência).
Qualquer outro compilador que eu testei (clang, icc, vc) gera os armazenamentos esperados, com qualquer versão do compilador e qualquer tamanho de array. Então, neste ponto, estou me perguntando, este é um bug do compilador GCC (muito antigo e grave?) Ou a definição de volátil no padrão imprecisa que este é realmente um comportamento em conformidade, tornando essencialmente impossível escrever um portátil " Função SecureZeroMemory "?
Edit: Algumas observações interessantes.
#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;
}
A possível gravação de callMeMaybe () fará com que todas as versões do GCC, exceto 6.1, gerem os armazenamentos esperados. Comentar na cerca de memória também fará com que o GCC 6.1 gere os armazenamentos, embora apenas em combinação com a possível escrita de callMeMaybe ().
Alguém também sugeriu esvaziar os caches. A Microsoft não tenta liberar o cache em "SecureZeroMemory". O cache provavelmente será invalidado muito rápido de qualquer maneira, então isso provavelmente não será um grande problema. Além disso, se outro programa estivesse tentando sondar os dados, ou se fosse gravado no arquivo de página, seria sempre a versão zerada.
Existem também algumas preocupações sobre o GCC 6.1 usando memset () na função autônoma. O compilador GCC 6.1 no godbolt pode ter uma compilação quebrada, já que o GCC 6.1 parece gerar um loop normal (como o 5.3 faz no godbolt) para a função autônoma para algumas pessoas. (Leia os comentários da resposta de zwol.)
volatile
é um bug, a menos que se prove o contrário. Mas provavelmente um bug.volatile
é tão subespecificado que pode ser perigoso - apenas não o use.