Manchmal müssen wir aus Sicherheitsgründen den Speicher auf Null setzen, um einen unbeabsichtigten Zugriff auf vertrauliche Daten zu verhindern, beispielsweise um einen Schlüssel nach dem Verschlüsseln einiger Daten sicher zu löschen. Die meisten Leute schlagen vor, zufällige Daten in ein Array zu schreiben, das die vertraulichen Informationen enthält, da dies von einem Compiler nicht optimiert werden kann. Es versteht sich von selbst, dass die naive Verwendung von Funktionen wie memset
durch einen optimierenden Compiler aufgrund der Als-ob-Regel optimiert werden kann, wenn es sich um die letzte Operation handelt, die an den Daten ausgeführt wird, bevor sie den Gültigkeitsbereich verlassen. Das Abrufen und Schreiben von zufälligen Daten ist jedoch langsam, und ich habe möglicherweise eine Lösung gefunden. Ich möchte eine Expertenmeinung, bevor ich sie im Produktionscode bereitstelle.
Wenn Sie irgendetwas mit sich selbst verknüpfen, ergibt sich aufgrund der Natur des Bedieners immer ein Wert von Null, und es ist sehr schnell. Das Durchlaufen eines Speicherblocks und das Xoring mit sich selbst scheint eine sehr effektive Lösung für das Problem der Nullung zu sein, aber ich befürchte, dass er durch einen ausreichend guten optimierenden Compiler wegoptimiert werden könnte. Es ist plattformübergreifend und portabel und erfordert keine Verwendung der Standardbibliothek, außer für die Verwendung des size_t
Datentyps. Ich habe eine Referenzimplementierung dessen beigefügt, was ich unten meine. Darin habe ich eine aufgerufene Funktion, nuke
die einen Zeiger data_to_zero
und iterativ xors size
Bytes mit sich nimmt.
void nuke (void *data_to_zero, size_t size)
{
size_t i;
for (i = 0; i < size; i++) {
((unsigned char*)data_to_zero)[i] ^= ((unsigned char*)data_to_zero)[i];
}
}
Diese Implementierung ist ziemlich langsam, aber wesentlich schneller als das Erfassen und Schreiben ausreichend zufälliger Daten data
. Nach der Optimierung ist es schneller als die memset
Implementierungen, auf die ich sowieso zugreifen kann, was überraschend ist.
Ich habe Assembly noch nicht gelernt, aber die Assembly-Ausgabe nach der Optimierung mit GCC und Clang auf O2- und O3-Ebene eines 64-Bit-x86-Prozessors enthält die xorl
Anweisung irgendwo im Code, manchmal zweimal. Das zeigt mir, dass die Speicherung tatsächlich stattfindet, aber ich möchte, dass jemand, der weiß, wovon er spricht, dies bestätigt.
Ist das eine praktikable Lösung?
Der richtige Weg, dies zu tun, besteht darin, die memset_s()
Funktion aufzurufen . Es verwendet das flüchtige Typqualifikationsmerkmal, um den Compiler darüber zu informieren, dass der Aufruf der Funktion memset_s () nicht optimiert werden sollte.
Leider ist diese Lösung aufgrund der Art des flüchtigen Typs, der vor allen Arten von Optimierungen schützt, möglicherweise nicht so effizient wie möglich. Sie kann den Compiler daran hindern, die optimalen Montageanweisungen zu verwenden, und kann zu weniger effizientem Code führen. Ein weiteres Problem memset_s()
ist, dass es in C11 eingeführt wurde.
Wenn Sie nicht verwenden können memset_s()
, müssen Sie eine der folgenden Methoden in Betracht ziehen:
*(volatile char*)pwd= *(volatile char*)pwd
. Das Problem bei dieser Lösung ist, dass sie möglicherweise nicht für alle Implementierungen funktioniert.memset_s()
(BEISPIEL 1). Das Problem dabei ist, dass es auch immer noch nicht garantiert funktioniert - Der C-Standard besagt, dass Zugriffe auf flüchtige Objekte Teil des unveränderlichen beobachtbaren Verhaltens sind -, sagt jedoch nichts über Zugriffe über lvalue-Ausdrücke mit flüchtigen Typen ausAls Fazit - was auch immer Sie wählen, es wird dringend empfohlen, immer den resultierenden Assemdbly-Code zu überprüfen, um sicherzustellen, dass der Speicher tatsächlich gelöscht wird und keiner der Speicheraufrufe optimiert wird.
BEISPIEL 1.
static void secure_memzero(void * p, size_t len)
{
volatile uint8_t * _p = p;
while (len--) *_p++ = 0;
}
BEISPIEL 2.
static void * (* const volatile memset_ptr)(void *, int, size_t) = memset;
static void secure_memzero(void * p, size_t len)
{
(memset_ptr)(p, 0, len);
}
void
dosomethingsensitive(void)
{
uint8_t key[32];
...
/* Zero sensitive information. */
secure_memzero(key, sizeof(key));
}
Dieser Artikel stammt aus dem Internet. Bitte geben Sie beim Nachdruck die Quelle an.
Bei Verstößen wenden Sie sich bitte [email protected] Löschen.
Lass mich ein paar Worte sagen