Necesito una función que (como SecureZeroMemory de WinAPI) siempre ponga a cero la memoria y no se optimice, incluso si el compilador piensa que la memoria nunca se volverá a acceder después de eso. Parece un candidato perfecto para volátiles. Pero estoy teniendo algunos problemas para que esto funcione con GCC. Aquí hay una función de ejemplo:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Suficientemente simple. Pero el código que GCC realmente genera si lo llama varía enormemente con la versión del compilador y la cantidad de bytes que está intentando poner a cero. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 y 4.5.3 nunca ignoran lo volátil.
- GCC 4.6.4 y 4.7.3 ignoran los volátiles para los tamaños de matriz 1, 2 y 4.
- GCC 4.8.1 hasta 4.9.2 ignoran volátil para los tamaños de matriz 1 y 2.
- GCC 5.1 hasta 5.3 ignoran los volátiles para los tamaños de matriz 1, 2, 4, 8.
- GCC 6.1 simplemente lo ignora para cualquier tamaño de matriz (puntos de bonificación por coherencia).
Cualquier otro compilador que haya probado (clang, icc, vc) genera las tiendas que uno esperaría, con cualquier versión del compilador y cualquier tamaño de matriz. Entonces, en este punto, me pregunto, ¿se trata de un error del compilador de GCC (bastante antiguo y severo?), O la definición de volátil en el estándar es tan imprecisa que esto es realmente un comportamiento conforme, lo que hace que sea esencialmente imposible escribir un portátil " ¿Función SecureZeroMemory "?
Editar: Algunas observaciones interesantes.
#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;
}
La posible escritura desde callMeMaybe () hará que todas las versiones de GCC excepto la 6.1 generen las tiendas esperadas.Comentar en la valla de memoria también hará que GCC 6.1 genere las tiendas, aunque solo en combinación con la posible escritura de callMeMaybe ().
Alguien también sugirió vaciar los cachés. Microsoft no intenta vaciar la caché en absoluto en "SecureZeroMemory".Es probable que la caché se invalide bastante rápido de todos modos, por lo que probablemente esto no sea un gran problema. Además, si otro programa intentaba sondear los datos, o si se iban a escribir en el archivo de paginación, siempre sería la versión puesta a cero.
También existen algunas preocupaciones sobre el uso de memset () en GCC 6.1 en la función independiente. El compilador GCC 6.1 en godbolt podría tener una compilación rota, ya que GCC 6.1 parece generar un bucle normal (como lo hace 5.3 en godbolt) para la función independiente para algunas personas. (Lea los comentarios de la respuesta de zwol).
volatile
es un error a menos que se demuestre lo contrario. Pero lo más probable es que sea un error.volatile
está tan subespecificado como para ser peligroso, simplemente no lo use.